Add an option to set a separate password for backups and exports

This commit is contained in:
Alexander Bakker 2022-06-05 18:26:46 +02:00
parent d40ebf0f67
commit a492bcbde3
14 changed files with 243 additions and 33 deletions

View file

@ -9,12 +9,16 @@ import android.os.ParcelFileDescriptor;
import android.util.Log; import android.util.Log;
import com.beemdevelopment.aegis.util.IOUtils; import com.beemdevelopment.aegis.util.IOUtils;
import com.beemdevelopment.aegis.vault.VaultFile;
import com.beemdevelopment.aegis.vault.VaultRepository; import com.beemdevelopment.aegis.vault.VaultRepository;
import com.beemdevelopment.aegis.vault.VaultRepositoryException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
public class AegisBackupAgent extends BackupAgent { public class AegisBackupAgent extends BackupAgent {
private static final String TAG = AegisBackupAgent.class.getSimpleName(); private static final String TAG = AegisBackupAgent.class.getSimpleName();
@ -46,14 +50,17 @@ public class AegisBackupAgent extends BackupAgent {
} }
// first copy the vault to the files/backup directory // first copy the vault to the files/backup directory
createBackupDir();
File vaultBackupFile = getVaultBackupFile(); File vaultBackupFile = getVaultBackupFile();
try { try (OutputStream outputStream = new FileOutputStream(vaultBackupFile)) {
VaultRepository.copyFileTo(this, vaultBackupFile); createBackupDir();
} catch (IOException e) {
VaultFile vaultFile = VaultRepository.readVaultFile(this);
byte[] bytes = vaultFile.exportable().toBytes();
outputStream.write(bytes);
} catch (VaultRepositoryException | IOException e) {
Log.e(TAG, String.format("onFullBackup() failed: %s", e)); Log.e(TAG, String.format("onFullBackup() failed: %s", e));
deleteBackupDir(); deleteBackupDir();
throw e; throw new IOException(e);
} }
// then call the original implementation so that fullBackupContent specified in AndroidManifest is read // then call the original implementation so that fullBackupContent specified in AndroidManifest is read

View file

@ -114,7 +114,7 @@ public class Dialogs {
.create()); .create());
} }
public static void showSetPasswordDialog(ComponentActivity activity, Dialogs.SlotListener listener) { public static void showSetPasswordDialog(ComponentActivity activity, PasswordSlotListener listener) {
Zxcvbn zxcvbn = new Zxcvbn(); Zxcvbn zxcvbn = new Zxcvbn();
View view = activity.getLayoutInflater().inflate(R.layout.dialog_password, null); View view = activity.getLayoutInflater().inflate(R.layout.dialog_password, null);
EditText textPassword = view.findViewById(R.id.text_password); EditText textPassword = view.findViewById(R.id.text_password);
@ -460,8 +460,8 @@ public class Dialogs {
void onTextInputResult(char[] text); void onTextInputResult(char[] text);
} }
public interface SlotListener { public interface PasswordSlotListener {
void onSlotResult(Slot slot, Cipher cipher); void onSlotResult(PasswordSlot slot, Cipher cipher);
void onException(Exception e); void onException(Exception e);
} }

View file

@ -30,7 +30,7 @@ import com.beemdevelopment.aegis.vault.VaultBackupManager;
import com.beemdevelopment.aegis.vault.VaultFileCredentials; import com.beemdevelopment.aegis.vault.VaultFileCredentials;
import com.beemdevelopment.aegis.vault.VaultRepository; import com.beemdevelopment.aegis.vault.VaultRepository;
import com.beemdevelopment.aegis.vault.VaultRepositoryException; import com.beemdevelopment.aegis.vault.VaultRepositoryException;
import com.beemdevelopment.aegis.vault.slots.Slot; import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
import com.beemdevelopment.aegis.vault.slots.SlotException; import com.beemdevelopment.aegis.vault.slots.SlotException;
import java.io.File; import java.io.File;
@ -267,9 +267,9 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
if (_vaultManager.getVault().isEncryptionEnabled()) { if (_vaultManager.getVault().isEncryptionEnabled()) {
cb.exportVault(stream -> _vaultManager.getVault().export(stream)); cb.exportVault(stream -> _vaultManager.getVault().export(stream));
} else { } else {
Dialogs.showSetPasswordDialog(requireActivity(), new Dialogs.SlotListener() { Dialogs.showSetPasswordDialog(requireActivity(), new Dialogs.PasswordSlotListener() {
@Override @Override
public void onSlotResult(Slot slot, Cipher cipher) { public void onSlotResult(PasswordSlot slot, Cipher cipher) {
VaultFileCredentials creds = new VaultFileCredentials(); VaultFileCredentials creds = new VaultFileCredentials();
try { try {

View file

@ -24,6 +24,7 @@ import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
import com.beemdevelopment.aegis.ui.preferences.SwitchPreference; import com.beemdevelopment.aegis.ui.preferences.SwitchPreference;
import com.beemdevelopment.aegis.ui.tasks.PasswordSlotDecryptTask; import com.beemdevelopment.aegis.ui.tasks.PasswordSlotDecryptTask;
import com.beemdevelopment.aegis.vault.VaultFileCredentials; import com.beemdevelopment.aegis.vault.VaultFileCredentials;
import com.beemdevelopment.aegis.vault.VaultRepository;
import com.beemdevelopment.aegis.vault.VaultRepositoryException; import com.beemdevelopment.aegis.vault.VaultRepositoryException;
import com.beemdevelopment.aegis.vault.slots.BiometricSlot; import com.beemdevelopment.aegis.vault.slots.BiometricSlot;
import com.beemdevelopment.aegis.vault.slots.PasswordSlot; import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
@ -33,6 +34,7 @@ import com.beemdevelopment.aegis.vault.slots.SlotList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.crypto.Cipher; import javax.crypto.Cipher;
@ -43,6 +45,8 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
private Preference _setPasswordPreference; private Preference _setPasswordPreference;
private Preference _passwordReminderPreference; private Preference _passwordReminderPreference;
private SwitchPreferenceCompat _pinKeyboardPreference; private SwitchPreferenceCompat _pinKeyboardPreference;
private SwitchPreference _backupPasswordPreference;
private Preference _backupPasswordChangePreference;
@Override @Override
public void onResume() { public void onResume() {
@ -161,7 +165,7 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
Dialogs.showPasswordInputDialog(requireContext(), R.string.set_password_confirm, R.string.pin_keyboard_description, password -> { Dialogs.showPasswordInputDialog(requireContext(), R.string.set_password_confirm, R.string.pin_keyboard_description, password -> {
if (isDigitsOnly(new String(password))) { if (isDigitsOnly(new String(password))) {
List<PasswordSlot> slots = _vaultManager.getVault().getCredentials().getSlots().findAll(PasswordSlot.class); List<PasswordSlot> slots = getRegularPasswordSlots(_vaultManager.getVault().getCredentials().getSlots());
PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password); PasswordSlotDecryptTask.Params params = new PasswordSlotDecryptTask.Params(slots, password);
PasswordSlotDecryptTask task = new PasswordSlotDecryptTask(requireContext(), new PasswordConfirmationListener()); PasswordSlotDecryptTask task = new PasswordSlotDecryptTask(requireContext(), new PasswordConfirmationListener());
task.execute(getLifecycle(), params); task.execute(getLifecycle(), params);
@ -232,33 +236,88 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
Dialogs.showSecureDialog(builder.create()); Dialogs.showSecureDialog(builder.create());
return false; return false;
}); });
_backupPasswordPreference = requirePreference("pref_backup_password");
_backupPasswordPreference.setOnPreferenceChangeListener((preference, newValue) -> {
if (!isBackupPasswordSet()) {
Dialogs.showSetPasswordDialog(requireActivity(), new SetBackupPasswordListener());
} else {
SlotList slots = _vaultManager.getVault().getCredentials().getSlots();
for (Slot slot : getBackupPasswordSlots(slots)) {
slots.remove(slot);
}
saveAndBackupVault();
updateEncryptionPreferences();
}
return false;
});
_backupPasswordChangePreference = requirePreference("pref_backup_password_change");
_backupPasswordChangePreference.setOnPreferenceClickListener(preference -> {
Dialogs.showSetPasswordDialog(requireActivity(), new SetBackupPasswordListener());
return false;
});
} }
private void updateEncryptionPreferences() { private void updateEncryptionPreferences() {
boolean encrypted = _vaultManager.getVault().isEncryptionEnabled(); boolean encrypted = _vaultManager.getVault().isEncryptionEnabled();
boolean backupPasswordSet = isBackupPasswordSet();
_encryptionPreference.setChecked(encrypted, true); _encryptionPreference.setChecked(encrypted, true);
_setPasswordPreference.setVisible(encrypted); _setPasswordPreference.setVisible(encrypted);
_biometricsPreference.setVisible(encrypted); _biometricsPreference.setVisible(encrypted);
_autoLockPreference.setVisible(encrypted); _autoLockPreference.setVisible(encrypted);
_pinKeyboardPreference.setVisible(encrypted); _pinKeyboardPreference.setVisible(encrypted);
_backupPasswordPreference.getParent().setVisible(encrypted);
_backupPasswordPreference.setChecked(backupPasswordSet, true);
_backupPasswordChangePreference.setVisible(backupPasswordSet);
if (encrypted) { if (encrypted) {
SlotList slots = _vaultManager.getVault().getCredentials().getSlots(); SlotList slots = _vaultManager.getVault().getCredentials().getSlots();
boolean multiPassword = slots.findAll(PasswordSlot.class).size() > 1; boolean multiBackupPassword = getBackupPasswordSlots(slots).size() > 1;
boolean multiPassword = getRegularPasswordSlots(slots).size() > 1;
boolean multiBio = slots.findAll(BiometricSlot.class).size() > 1; boolean multiBio = slots.findAll(BiometricSlot.class).size() > 1;
boolean canUseBio = BiometricsHelper.isAvailable(requireContext()); boolean canUseBio = BiometricsHelper.isAvailable(requireContext());
_setPasswordPreference.setEnabled(!multiPassword); _setPasswordPreference.setEnabled(!multiPassword);
_biometricsPreference.setEnabled(canUseBio && !multiBio); _biometricsPreference.setEnabled(canUseBio && !multiBio);
_biometricsPreference.setChecked(slots.has(BiometricSlot.class), true); _biometricsPreference.setChecked(slots.has(BiometricSlot.class), true);
_passwordReminderPreference.setVisible(slots.has(BiometricSlot.class)); _passwordReminderPreference.setVisible(slots.has(BiometricSlot.class));
_backupPasswordChangePreference.setEnabled(!multiBackupPassword);
} else { } else {
_setPasswordPreference.setEnabled(false); _setPasswordPreference.setEnabled(false);
_biometricsPreference.setEnabled(false); _biometricsPreference.setEnabled(false);
_biometricsPreference.setChecked(false, true); _biometricsPreference.setChecked(false, true);
_passwordReminderPreference.setVisible(false); _passwordReminderPreference.setVisible(false);
_backupPasswordChangePreference.setEnabled(false);
} }
} }
private boolean isBackupPasswordSet() {
VaultRepository vault = _vaultManager.getVault();
if (!vault.isEncryptionEnabled()) {
return false;
}
return getBackupPasswordSlots(vault.getCredentials().getSlots()).size() > 0;
}
private static List<PasswordSlot> getBackupPasswordSlots(SlotList slots) {
return getPasswordSlots(slots, true);
}
private static List<PasswordSlot> getRegularPasswordSlots(SlotList slots) {
return getPasswordSlots(slots, false);
}
private static List<PasswordSlot> getPasswordSlots(SlotList slots, boolean isBackup) {
return slots
.findAll(PasswordSlot.class)
.stream()
.filter(s -> s.isBackup() == isBackup)
.collect(Collectors.toList());
}
private String getPasswordReminderSummary() { private String getPasswordReminderSummary() {
PassReminderFreq freq = _prefs.getPasswordReminderFrequency(); PassReminderFreq freq = _prefs.getPasswordReminderFrequency();
if (freq == PassReminderFreq.NEVER) { if (freq == PassReminderFreq.NEVER) {
@ -291,9 +350,9 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
return getString(R.string.pref_auto_lock_summary, builder.toString()); return getString(R.string.pref_auto_lock_summary, builder.toString());
} }
private class SetPasswordListener implements Dialogs.SlotListener { private class SetPasswordListener implements Dialogs.PasswordSlotListener {
@Override @Override
public void onSlotResult(Slot slot, Cipher cipher) { public void onSlotResult(PasswordSlot slot, Cipher cipher) {
VaultFileCredentials creds = _vaultManager.getVault().getCredentials(); VaultFileCredentials creds = _vaultManager.getVault().getCredentials();
SlotList slots = creds.getSlots(); SlotList slots = creds.getSlots();
@ -331,6 +390,43 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
} }
} }
private class SetBackupPasswordListener implements Dialogs.PasswordSlotListener {
@Override
public void onSlotResult(PasswordSlot slot, Cipher cipher) {
slot.setIsBackup(true);
VaultFileCredentials creds = _vaultManager.getVault().getCredentials();
SlotList slots = creds.getSlots();
try {
// encrypt the master key for this slot
slot.setKey(creds.getKey(), cipher);
// remove the old backup password slot
for (Slot oldSlot : getBackupPasswordSlots(slots)) {
slots.remove(oldSlot);
}
// add the new backup password slot
slots.add(slot);
} catch (SlotException e) {
onException(e);
return;
}
_vaultManager.getVault().setCredentials(creds);
saveAndBackupVault();
updateEncryptionPreferences();
}
@Override
public void onException(Exception e) {
e.printStackTrace();
updateEncryptionPreferences();
Dialogs.showErrorDialog(requireContext(), R.string.encryption_set_password_error, e);
}
}
private class RegisterBiometricsListener implements BiometricSlotInitializer.Listener { private class RegisterBiometricsListener implements BiometricSlotInitializer.Listener {
@Override @Override
public void onInitializeSlot(BiometricSlot slot, Cipher cipher) { public void onInitializeSlot(BiometricSlot slot, Cipher cipher) {
@ -357,9 +453,9 @@ public class SecurityPreferencesFragment extends PreferencesFragment {
} }
} }
private class EnableEncryptionListener implements Dialogs.SlotListener { private class EnableEncryptionListener implements Dialogs.PasswordSlotListener {
@Override @Override
public void onSlotResult(Slot slot, Cipher cipher) { public void onSlotResult(PasswordSlot slot, Cipher cipher) {
VaultFileCredentials creds = new VaultFileCredentials(); VaultFileCredentials creds = new VaultFileCredentials();
try { try {

View file

@ -117,6 +117,21 @@ public class VaultFile {
} }
} }
/**
* Returns a copy of this VaultFile that's suitable for exporting.
* In case there's a backup password slot, any regular password slots are stripped.
*/
public VaultFile exportable() {
if (!isEncrypted()) {
return this;
}
return new VaultFile(getContent(), new VaultFile.Header(
getHeader().getSlots().exportable(),
getHeader().getParams()
));
}
public static class Header { public static class Header {
private SlotList _slots; private SlotList _slots;
private CryptParameters _params; private CryptParameters _params;

View file

@ -37,4 +37,12 @@ public class VaultFileCredentials implements Serializable {
public SlotList getSlots() { public SlotList getSlots() {
return _slots; return _slots;
} }
/**
* Returns a copy of these VaultFileCredentials that is suitable for exporting.
* In case there's a backup password slot, any regular password slots are stripped.
*/
public VaultFileCredentials exportable() {
return new VaultFileCredentials(_key, _slots.exportable());
}
} }

View file

@ -19,7 +19,9 @@ import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -198,7 +200,10 @@ public class VaultManager {
} }
File tempFile = File.createTempFile(VaultBackupManager.FILENAME_PREFIX, ".json", dir); File tempFile = File.createTempFile(VaultBackupManager.FILENAME_PREFIX, ".json", dir);
VaultRepository.copyFileTo(_context, tempFile); try (OutputStream outStream = new FileOutputStream(tempFile)) {
_repo.export(outStream);
}
_backups.scheduleBackup(tempFile, _prefs.getBackupsLocation(), _prefs.getBackupsVersionCount()); _backups.scheduleBackup(tempFile, _prefs.getBackupsLocation(), _prefs.getBackupsVersionCount());
} catch (IOException e) { } catch (IOException e) {
throw new VaultRepositoryException(e); throw new VaultRepositoryException(e);

View file

@ -107,13 +107,6 @@ public class VaultRepository {
return new VaultRepository(context, vault, creds); return new VaultRepository(context, vault, creds);
} }
public static void copyFileTo(Context context, File destFile) throws IOException {
try (InputStream inStream = VaultRepository.getAtomicFile(context).openRead();
OutputStream outStream = new FileOutputStream(destFile)) {
IOUtils.copy(inStream, outStream);
}
}
void save() throws VaultRepositoryException { void save() throws VaultRepositoryException {
try { try {
JSONObject obj = _vault.toJson(); JSONObject obj = _vault.toJson();
@ -137,7 +130,7 @@ public class VaultRepository {
} }
/** /**
* Exports the vault bt serializing it and writing it to the given OutputStream. If encryption * Exports the vault by serializing it and writing it to the given OutputStream. If encryption
* is enabled, the vault will be encrypted automatically. * is enabled, the vault will be encrypted automatically.
*/ */
public void export(OutputStream stream) throws VaultRepositoryException { public void export(OutputStream stream) throws VaultRepositoryException {
@ -149,6 +142,10 @@ public class VaultRepository {
* not null, it will be used to encrypt the vault first. * not null, it will be used to encrypt the vault first.
*/ */
public void export(OutputStream stream, VaultFileCredentials creds) throws VaultRepositoryException { public void export(OutputStream stream, VaultFileCredentials creds) throws VaultRepositoryException {
if (creds != null) {
creds = creds.exportable();
}
try { try {
VaultFile vaultFile = new VaultFile(); VaultFile vaultFile = new VaultFile();
if (creds != null) { if (creds != null) {

View file

@ -16,16 +16,19 @@ import javax.crypto.SecretKey;
public class PasswordSlot extends RawSlot { public class PasswordSlot extends RawSlot {
private boolean _repaired; private boolean _repaired;
private boolean _isBackup;
private SCryptParameters _params; private SCryptParameters _params;
public PasswordSlot() { public PasswordSlot() {
super(); super();
} }
protected PasswordSlot(UUID uuid, byte[] key, CryptParameters keyParams, SCryptParameters scryptParams, boolean repaired) { protected PasswordSlot(UUID uuid, byte[] key, CryptParameters keyParams, SCryptParameters scryptParams, boolean repaired, boolean isBackup) {
super(uuid, key, keyParams); super(uuid, key, keyParams);
_params = scryptParams; _params = scryptParams;
_repaired = repaired; _repaired = repaired;
_isBackup = isBackup;
} }
@Override @Override
@ -37,6 +40,7 @@ public class PasswordSlot extends RawSlot {
obj.put("p", _params.getP()); obj.put("p", _params.getP());
obj.put("salt", Hex.encode(_params.getSalt())); obj.put("salt", Hex.encode(_params.getSalt()));
obj.put("repaired", _repaired); obj.put("repaired", _repaired);
obj.put("is_backup", _isBackup);
return obj; return obj;
} catch (JSONException e) { } catch (JSONException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -70,8 +74,15 @@ public class PasswordSlot extends RawSlot {
return _repaired; return _repaired;
} }
public SCryptParameters getSCryptParameters() { /**
return _params; * Reports whether this slot is a backup password slot.
*/
public boolean isBackup() {
return _isBackup;
}
public void setIsBackup(boolean isBackup) {
_isBackup = isBackup;
} }
@Override @Override

View file

@ -140,7 +140,8 @@ public abstract class Slot extends UUIDMap.Value {
Hex.decode(obj.getString("salt")) Hex.decode(obj.getString("salt"))
); );
boolean repaired = obj.optBoolean("repaired", false); boolean repaired = obj.optBoolean("repaired", false);
slot = new PasswordSlot(uuid, key, keyParams, scryptParams, repaired); boolean isBackup = obj.optBoolean("is_backup", false);
slot = new PasswordSlot(uuid, key, keyParams, scryptParams, repaired, isBackup);
break; break;
case Slot.TYPE_BIOMETRIC: case Slot.TYPE_BIOMETRIC:
slot = new BiometricSlot(uuid, key, keyParams); slot = new BiometricSlot(uuid, key, keyParams);

View file

@ -57,4 +57,31 @@ public class SlotList extends UUIDMap<Slot> {
public <T extends Slot> boolean has(Class<T> type) { public <T extends Slot> boolean has(Class<T> type) {
return find(type) != null; return find(type) != null;
} }
/**
* Returns a copy of this SlotList that is suitable for exporting.
* In case there's a backup password slot, any regular password slots are stripped.
*/
public SlotList exportable() {
boolean hasBackupSlots = false;
for (Slot slot : this) {
if (slot instanceof PasswordSlot && ((PasswordSlot) slot).isBackup()) {
hasBackupSlots = true;
break;
}
}
if (!hasBackupSlots) {
return this;
}
SlotList slots = new SlotList();
for (Slot slot : this) {
if (!(slot instanceof PasswordSlot) || ((PasswordSlot) slot).isBackup()) {
slots.add(slot);
}
}
return slots;
}
} }

View file

@ -76,6 +76,11 @@
<string name="pref_auto_lock_type_back_button">The back button is pressed</string> <string name="pref_auto_lock_type_back_button">The back button is pressed</string>
<string name="pref_auto_lock_type_minimize">The app is minimized</string> <string name="pref_auto_lock_type_minimize">The app is minimized</string>
<string name="pref_auto_lock_type_device_lock">The device is locked</string> <string name="pref_auto_lock_type_device_lock">The device is locked</string>
<string name="pref_backup_password_category">Backup &amp; Export</string>
<string name="pref_backup_password_title">Separate password for backup &amp; export</string>
<string name="pref_backup_password_summary">If enabled, the password that is used to unlock the app can\'t be used to decrypt backups and exports anymore.</string>
<string name="pref_backup_password_change_title">Change password for backup &amp; export</string>
<string name="pref_backup_password_change_summary">Set a new password that\'ll be used to encrypt the vault during backup and export.</string>
<string name="pref_encryption_title">Encryption</string> <string name="pref_encryption_title">Encryption</string>
<string name="pref_encryption_summary">Encrypt the vault and unlock it with a password or biometrics</string> <string name="pref_encryption_summary">Encrypt the vault and unlock it with a password or biometrics</string>
<string name="pref_biometrics_title">Biometric unlock</string> <string name="pref_biometrics_title">Biometric unlock</string>

View file

@ -33,6 +33,23 @@
android:dependency="pref_biometrics" android:dependency="pref_biometrics"
app:iconSpaceReserved="false"/> app:iconSpaceReserved="false"/>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:title="@string/pref_backup_password_category"
app:iconSpaceReserved="false">
<com.beemdevelopment.aegis.ui.preferences.SwitchPreference
android:key="pref_backup_password"
android:title="@string/pref_backup_password_title"
android:summary="@string/pref_backup_password_summary"
android:persistent="false"
app:iconSpaceReserved="false"/>
<Preference
android:key="pref_backup_password_change"
android:title="@string/pref_backup_password_change_title"
android:summary="@string/pref_backup_password_change_summary"
android:dependency="pref_encryption"
app:iconSpaceReserved="false"/>
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/pref_section_behavior_title" android:title="@string/pref_section_behavior_title"
app:iconSpaceReserved="false"> app:iconSpaceReserved="false">

View file

@ -1,5 +1,9 @@
package com.beemdevelopment.aegis.vault.slots; package com.beemdevelopment.aegis.vault.slots;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import com.beemdevelopment.aegis.crypto.CryptoUtils; import com.beemdevelopment.aegis.crypto.CryptoUtils;
import com.beemdevelopment.aegis.crypto.MasterKey; import com.beemdevelopment.aegis.crypto.MasterKey;
import com.beemdevelopment.aegis.crypto.SCryptParameters; import com.beemdevelopment.aegis.crypto.SCryptParameters;
@ -16,9 +20,6 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
public class SlotTest { public class SlotTest {
private MasterKey _masterKey; private MasterKey _masterKey;
@ -93,4 +94,24 @@ public class SlotTest {
garbledCiphertext[0] = (byte) ~garbledCiphertext[0]; garbledCiphertext[0] = (byte) ~garbledCiphertext[0];
assertThrows(SlotIntegrityException.class, () -> slot.getKey(decryptCipher)); assertThrows(SlotIntegrityException.class, () -> slot.getKey(decryptCipher));
} }
@Test
public void testPasswordSlotExclusion() {
SlotList slots = new SlotList();
PasswordSlot passSlot = new PasswordSlot();
PasswordSlot passSlot2 = new PasswordSlot();
slots.add(passSlot);
slots.add(passSlot2);
assertArrayEquals(slots.getValues().toArray(), slots.exportable().getValues().toArray());
SlotList backupSlots = new SlotList();
PasswordSlot backupSlot = new PasswordSlot();
backupSlot.setIsBackup(true);
slots.add(backupSlot);
backupSlots.add(backupSlot);
assertArrayEquals(backupSlots.getValues().toArray(), slots.exportable().getValues().toArray());
assertNotEquals(slots.getValues().toArray(), slots.exportable().getValues().toArray());
}
} }