Skip to content

Commit

Permalink
Overwrite master key in keystore on restore
Browse files Browse the repository at this point in the history
  • Loading branch information
justin-stephenson committed Jan 13, 2025
1 parent ec103b4 commit b40862d
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,77 @@ public void addRemoveTokensBackupRestore() throws GeneralSecurityException,
validateTokens(cur, a2);
}

private void copySP(SharedPreferences copyFrom, SharedPreferences copyTo) {
// Copy to
SharedPreferences.Editor ed = copyTo.edit();
// Copy from
SharedPreferences sp = copyFrom;
ed.clear();
for (Map.Entry<String, ?> entry : sp.getAll().entrySet()) {
Object v = entry.getValue();
String key = entry.getKey();
if (v instanceof String) {
ed.putString(key, ((String) v));
}
}
ed.commit();
}

@Test
// Test multiple restore passes
public void tokenBackupRestoreMultiPass() throws GeneralSecurityException,
IOException, Token.UnsafeUriException, Token.InvalidUriException, TokenPersistence.BadPasswordException, JSONException {
setup(null);
SharedPreferences cur = mContext.getSharedPreferences("tokenStore", android.content.Context.MODE_PRIVATE);
SharedPreferences bkp = mContext.getSharedPreferences("tokenBackup", Context.MODE_PRIVATE);
SharedPreferences bkpCopy = mContext.getSharedPreferences("tokenBackupCopy", Context.MODE_PRIVATE);

TokenPersistence tokenBackup = new TokenPersistence(mContext);
String origPwd = "backup";
String newPwd = "redhat";

tokenBackup.provision(origPwd);
Adapter a = new Adapter(mContext, this);

for (Map.Entry<String, String> entry : uris.entrySet()) {
Pair<SecretKey, Token> pair = Token.parse(entry.getValue());
a.add(pair.first, pair.second);
}

// Backup and Restore Pass 1
copySP(bkp, bkpCopy);
// Simulate app removal + reinstall
for (int i = 0; i < numTokens; i++) {
a.delete(0);
}
cur.edit().clear().commit();
assertEquals(0, cur.getAll().size());

// Setup new backup password
tokenBackup.provision(newPwd);

// Replace SP backup copy, must be after new provision to not overwrite MK
copySP(bkpCopy, bkp);
a.restoreTokens(origPwd);
validateTokens(cur, a);

// Pass 2: export and attempt restore again
copySP(bkp, bkpCopy);
for (int i = 0; i < numTokens; i++) {
a.delete(0);
}
cur.edit().clear().commit();
assertEquals(0, cur.getAll().size());

tokenBackup.provision(newPwd);
copySP(bkpCopy, bkp);
a.restoreTokens(origPwd);
String order = cur.getString("tokenOrder", null);
assertNotNull(order);

validateTokens(cur, a);
}

@Test
// Test restoring multiple migrated tokens
public void tokenCompatBackupRestore() throws GeneralSecurityException, IOException,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ public List<RestoredData> restore(String pwd) throws GeneralSecurityException,
throw new BadPasswordException();
}

// Overwrite the master key stored in the keystore, restored entries are then re-encrypted
KeyProtection kp = new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.build();

mKeyStore.setEntry(MASTER, new KeyStore.SecretKeyEntry(mk.decrypt(pwd)), kp);

for (Map.Entry<String, ?> item : mBackups.getAll().entrySet()) {
JSONObject obj;
String uuid = item.getKey();
Expand Down
2 changes: 1 addition & 1 deletion mobile/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
<string name="main_invalidated_title">Key invalidated!</string>
<string name="main_invalidated_message">The key for this token has been invalidated by the Android Key Store. This may have been due to a change in your lock screen settings. The token has been removed. Please contact your token provider.</string>
<string name="main_restore_title">Restore from backup</string>
<string name="main_restore_message">Please enter backup password</string>
<string name="main_restore_message">Please enter backup password\n\nUpon successful restore, this password used to restore will overwrite any previously setup backup password</string>
<string name="main_restore_bad_password">Invalid password</string>
<string name="main_restore_cancel_title">Cancel Restore?</string>
<string name="main_restore_cancel_message">You will lose all previously backed up tokens!</string>
Expand Down

0 comments on commit b40862d

Please sign in to comment.