Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ protected void onCreate(Bundle savedInstanceState) {
var appSecurityService = new AppSecurityService(getApplicationContext());

lockAction = new LockAction(this, appSecurityService);
generateNewKeyAction = new GenerateNewKeyAction(this, appSecurityService);
generateNewKeyAction = new GenerateNewKeyAction(this);

noteEditorLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), getOpenNoteResultCallback());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,19 @@ public void createPassword() {
char[] password = proceedPasswordSetting();

if (password != null) {
var context = activity.getApplicationContext();
var setupKeyActivityIntent = new Intent(context, SetupKeyActivity.class)
.putExtra(SetupKeyActivity.EXTRA_MODE, KeySetupMode.FIRST_RUN.toString());

try {
var passwordBytes = charsToBytes(password, StandardCharsets.UTF_8);
SecretCache.put(SetupKeyActivity.CACHE_KEY_PASSWORD, passwordBytes);
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}

var context = activity.getApplicationContext();
var setupKeyActivityIntent = new Intent(context, SetupKeyActivity.class)
.putExtra(SetupKeyActivity.EXTRA_MODE, KeySetupMode.FIRST_RUN.toString());

activity.startActivity(setupKeyActivityIntent);
activity.finish();
}
}

Expand All @@ -103,7 +104,7 @@ public void recoverKey() {
char[] hexKey = bytesToChars(hexKeyBytes,
StandardCharsets.UTF_8);

CryptoSecrets secrets = KeyUtils.getSecretsFromHex(hexKey, password);
CryptoSecrets secrets = KeyUtils.getSecretsFromKeyHexAndPassword(hexKey, password);
appSecurityService.unblockApp(secrets);
secrets.destroy();
} catch (AppSecurityException | CharacterCodingException e) {
Expand All @@ -112,6 +113,7 @@ public void recoverKey() {

activity.startActivity(new Intent(activity.getApplicationContext(),
NotesListActivity.class));
activity.finish();
}
}

Expand All @@ -129,6 +131,7 @@ public void changePassword() {

showToastMessage(R.string.updated);
activity.startActivity(new Intent(context, NotesListActivity.class));
activity.finish();
} catch (Exception e) {
throw new RuntimeException(e);
}
Expand All @@ -150,6 +153,7 @@ private char[] proceedPasswordSetting() {
}
} else {
if (Arrays.equals(password, createdPassword)) {
resetPassword();
return password;
} else {
showToastMessage(R.string.code_not_match);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,18 @@

package app.notesr.activity.security;

import static app.notesr.core.util.CharUtils.charsToBytes;

import android.content.Context;
import android.content.Intent;

import app.notesr.activity.ActivityBase;
import app.notesr.core.security.SecretCache;
import app.notesr.core.security.dto.CryptoSecrets;
import app.notesr.service.security.AppSecurityService;
import lombok.RequiredArgsConstructor;

import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

@RequiredArgsConstructor
public final class GenerateNewKeyAction {

private final ActivityBase activity;
private final AppSecurityService appSecurityService;

public void startActivity() {
CryptoSecrets secrets = appSecurityService.getActualSecrets();
char[] passwordChars = Arrays.copyOf(secrets.getPassword(), secrets.getPassword().length);

try {
byte[] passwordBytes = charsToBytes(passwordChars, StandardCharsets.UTF_8);
SecretCache.put(SetupKeyActivity.CACHE_KEY_PASSWORD, passwordBytes);
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}

secrets.destroy();

Context context = activity.getApplicationContext();
var intent = new Intent(context, SetupKeyActivity.class)
.putExtra(SetupKeyActivity.EXTRA_MODE, KeySetupMode.REGENERATION.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
import static java.util.Objects.requireNonNull;

import static app.notesr.core.util.ActivityUtils.showToastMessage;
import static app.notesr.core.util.CharUtils.bytesToChars;
import static app.notesr.core.util.CharUtils.charsToBytes;
import static app.notesr.core.util.KeyUtils.getSecretsFromHex;
import static app.notesr.core.util.KeyUtils.getKeyBytesFromKeyHex;

import android.os.Bundle;
import android.text.Editable;
Expand All @@ -24,25 +22,21 @@

import androidx.appcompat.app.ActionBar;

import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import app.notesr.R;
import app.notesr.activity.ActivityBase;
import app.notesr.core.security.SecretCache;
import app.notesr.core.security.dto.CryptoSecrets;
import app.notesr.core.util.CryptoSecretsValidator;

public final class ImportKeyActivity extends ActivityBase {

public static final String CACHE_KEY_HEX_KEY = "hexKey";
public static final String CACHE_KEY_PASSWORD = "password";
private static final String TAG = ImportKeyActivity.class.getCanonicalName();

private int resultCode = RESULT_CANCELED;
private EditText keyField;
private char[] hexKey;
private char[] password;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -55,8 +49,6 @@ protected void onCreate(Bundle savedInstanceState) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(getResources().getString(R.string.import_key));

password = getPasswordFromCache();

keyField = findViewById(R.id.importKeyField);
keyField.setImeOptions(IME_FLAG_NO_PERSONALIZED_LEARNING);

Expand All @@ -72,6 +64,7 @@ protected boolean requiresSession() {
@Override
public void finish() {
wipeUiFields();
wipeSensitiveClassFields();
setResult(resultCode);
super.finish();
}
Expand All @@ -84,49 +77,30 @@ private View.OnClickListener importKeyButtonOnClick() {
hexKeyEditable.getChars(0, hexKeyEditable.length(), hexKey, 0);

if (hexKey.length > 0) {
byte[] keyBytes;

try {
CryptoSecrets cryptoSecrets = getCryptoSecrets(hexKey, password);
cryptoSecrets.validate();
cryptoSecrets.destroy();
} catch (IllegalArgumentException | IllegalStateException e) {
keyBytes = getKeyBytesFromKeyHex(hexKey);
CryptoSecretsValidator.validateKey(keyBytes);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid key", e);
showToastMessage(this, getString(R.string.invalid_key),
Toast.LENGTH_SHORT);

return;
}

putResultsToCache(hexKey);

SecretCache.put(CACHE_KEY_HEX_KEY, keyBytes);
resultCode = RESULT_OK;
finish();
}
};
}

private CryptoSecrets getCryptoSecrets(char[] hexKey, char[] password) {
char[] hexKeyCopy = Arrays.copyOf(hexKey, hexKey.length);
return getSecretsFromHex(hexKeyCopy, password);
}

private void putResultsToCache(char[] hexKey) {
try {
SecretCache.put(CACHE_KEY_HEX_KEY, charsToBytes(hexKey,
StandardCharsets.UTF_8));
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}

SecretCache.removeIfExists(CACHE_KEY_PASSWORD); // Should be already removed
}

private char[] getPasswordFromCache() {
try {
byte[] passwordBytes = requireNonNull(SecretCache.take(CACHE_KEY_PASSWORD),
"Password missing in secret cache");
return bytesToChars(passwordBytes, StandardCharsets.UTF_8);
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
private void wipeSensitiveClassFields() {
if (hexKey != null) {
Arrays.fill(hexKey, '\0');
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import static app.notesr.core.util.ActivityUtils.disableBackButton;
import static app.notesr.core.util.ActivityUtils.showToastMessage;
import static app.notesr.core.util.CharUtils.charsToBytes;
import static app.notesr.core.util.KeyUtils.getKeyBytesFromHex;
import static app.notesr.core.util.KeyUtils.getKeyBytesFromKeyHex;

import android.content.Context;
import android.content.Intent;
Expand Down Expand Up @@ -96,7 +96,7 @@ private void apply(EditText hexKeyField, char[] hexKey)
throws IOException, NoSuchAlgorithmException {

char[] hexKeyCopy = Arrays.copyOf(hexKey, hexKey.length);
byte[] keyBytes = getKeyBytesFromHex(hexKeyCopy);
byte[] keyBytes = getKeyBytesFromKeyHex(hexKeyCopy);

Context context = getApplicationContext();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package app.notesr.activity.security;

import static app.notesr.core.util.CharUtils.bytesToChars;
import static app.notesr.core.util.CharUtils.charsToBytes;

import android.content.Context;
Expand Down Expand Up @@ -32,7 +33,7 @@ public final class KeySetupCompletionHandler {
private final ActivityBase activity;
private final AppSecurityService appSecurityService;
private final KeySetupMode mode;
private final CryptoSecrets cryptoSecrets;
private final byte[] keyBytes;

public void handle() {
switch (mode) {
Expand All @@ -44,7 +45,10 @@ public void handle() {

private void proceedFirstRun() {
try {
appSecurityService.setSecrets(cryptoSecrets);
char[] password = getCurrentPassword();

CryptoSecrets newSecrets = new CryptoSecrets(keyBytes, password);
appSecurityService.setSecrets(newSecrets);

Context context = activity.getApplicationContext();
Intent nextIntent = new Intent(context, NotesListActivity.class);
Expand Down Expand Up @@ -73,19 +77,15 @@ private void proceedRegeneration() {
.setTitle(R.string.warning)
.setPositiveButton(R.string.yes,
(dialog, which) -> onRegenerationConfirmed())
.setNegativeButton(R.string.no, null)
.setNegativeButton(R.string.no,
(dialog, which) -> onRegenerationCanceled())
.create()
.show();
}

private void onRegenerationConfirmed() {
byte[] keyBytes = Arrays.copyOf(cryptoSecrets.getKey(),
cryptoSecrets.getKey().length);

char[] password = Arrays.copyOf(cryptoSecrets.getPassword(),
cryptoSecrets.getPassword().length);

try {
char[] password = getCurrentPassword();
byte[] passwordBytes = charsToBytes(password, StandardCharsets.UTF_8);

SecretCache.put(SecretsUpdateAndroidService.NEW_KEY, keyBytes);
Expand All @@ -94,11 +94,36 @@ private void onRegenerationConfirmed() {
throw new RuntimeException(e);
}

cryptoSecrets.destroy();

Intent reEncryptionIntent = new Intent(activity.getApplicationContext(),
ReEncryptionActivity.class);

activity.startActivity(reEncryptionIntent);
activity.finish();
}

private void onRegenerationCanceled() {
if (keyBytes != null) {
Arrays.fill(keyBytes, (byte) 0);
}
}

private char[] getCurrentPassword() throws CharacterCodingException {
if (appSecurityService.isAuthConfigured()) {
CryptoSecrets cryptoSecrets = appSecurityService.getActualSecrets();

char[] password = cryptoSecrets.getPassword();
char[] passwordCopy = Arrays.copyOf(password, password.length);

cryptoSecrets.destroy();
return passwordCopy;
} else {
if (SecretCache.contains(SetupKeyActivity.CACHE_KEY_PASSWORD)) {
return bytesToChars(SecretCache.take(SetupKeyActivity.CACHE_KEY_PASSWORD),
StandardCharsets.UTF_8);
}
}

throw new IllegalStateException("App authentication is not configured" +
" and password is not found in cache");
}
}
Loading
Loading