Loading core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java +5 −3 Original line number Diff line number Diff line Loading @@ -423,19 +423,21 @@ public class RecoverableKeyStoreLoader { /** * Imports keys. * * @param sessionId Id for recovery session, same as in = {@link startRecoverySession}. * @param sessionId Id for recovery session, same as in * {@link #startRecoverySession(String, byte[], byte[], byte[], List)} on}. * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob * and session. KeyStore only uses package names from the application info in {@link * KeyEntryRecoveryData}. Caller is responsibility to perform certificates check. * @return Map from alias to raw key material. */ public void recoverKeys( public Map<String, byte[]> recoverKeys( @NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<KeyEntryRecoveryData> applicationKeys) throws RecoverableKeyStoreLoaderException { try { mBinder.recoverKeys( return (Map<String, byte[]>) mBinder.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys, UserHandle.getCallingUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); Loading core/java/com/android/internal/widget/ILockSettings.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -78,6 +78,6 @@ interface ILockSettings { byte[] startRecoverySession(in String sessionId, in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge, in List<KeyStoreRecoveryMetadata> secrets, int userId); void recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, in List<KeyEntryRecoveryData> applicationKeys, int userId); } services/core/java/com/android/server/locksettings/LockSettingsService.java +3 −3 Original line number Diff line number Diff line Loading @@ -2021,11 +2021,11 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override public void recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<KeyEntryRecoveryData> applicationKeys, @UserIdInt int userId) throws RemoteException { mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys, userId); return mRecoverableKeyStoreManager.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys, userId); } private static final String[] VALID_SETTINGS = new String[] { Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +11 −9 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; Loading Loading @@ -308,8 +309,6 @@ public class RecoverableKeyStoreManager { * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault * service. * * <p>TODO: should also load into AndroidKeyStore. * * @param sessionId The session ID used to generate the claim. See * {@link #startRecoverySession(String, byte[], byte[], byte[], List, int)}. * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault Loading @@ -317,9 +316,10 @@ public class RecoverableKeyStoreManager { * @param applicationKeys The encrypted key blobs returned by the remote vault service. These * were wrapped with the recovery key. * @param uid The uid of the recovery agent. * @return Map from alias to raw key material. * @throws RemoteException if an error occurred recovering the keys. */ public void recoverKeys( public Map<String, byte[]> recoverKeys( @NonNull String sessionId, @NonNull byte[] encryptedRecoveryKey, @NonNull List<KeyEntryRecoveryData> applicationKeys, Loading @@ -335,7 +335,7 @@ public class RecoverableKeyStoreManager { try { byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey); recoverApplicationKeys(recoveryKey, applicationKeys); return recoverApplicationKeys(recoveryKey, applicationKeys); } finally { sessionEntry.destroy(); mRecoverySessionStorage.remove(uid); Loading Loading @@ -370,20 +370,21 @@ public class RecoverableKeyStoreManager { /** * Uses {@code recoveryKey} to decrypt {@code applicationKeys}. * * <p>TODO: and load them into store? * * @return Map from alias to raw key material. * @throws RemoteException if an error occurred decrypting the keys. */ private void recoverApplicationKeys( private Map<String, byte[]> recoverApplicationKeys( @NonNull byte[] recoveryKey, @NonNull List<KeyEntryRecoveryData> applicationKeys) throws RemoteException { HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>(); for (KeyEntryRecoveryData applicationKey : applicationKeys) { String alias = new String(applicationKey.getAlias(), StandardCharsets.UTF_8); byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial(); try { // TODO: put decrypted key material in appropriate AndroidKeyStore byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial); keyMaterialByAlias.put(alias, keyMaterial); } catch (NoSuchAlgorithmException e) { // Should never happen: all the algorithms used are required by AOSP implementations throw new RemoteException( Loading @@ -399,6 +400,7 @@ public class RecoverableKeyStoreManager { /*writeableStackTrace=*/ true); } } return keyMaterialByAlias; } /** Loading services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +10 −6 Original line number Diff line number Diff line Loading @@ -285,7 +285,7 @@ public class RecoverableKeyStoreManagerTest { } @Test public void recoverKeys_doesNotThrowIfAllIsOk() throws Exception { public void recoverKeys_returnsDecryptedKeys() throws Exception { mRecoverableKeyStoreManager.startRecoverySession( TEST_SESSION_ID, TEST_PUBLIC_KEY, Loading @@ -302,16 +302,19 @@ public class RecoverableKeyStoreManagerTest { SecretKey recoveryKey = randomRecoveryKey(); byte[] encryptedClaimResponse = encryptClaimResponse( keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); byte[] applicationKeyBytes = randomBytes(32); KeyEntryRecoveryData applicationKey = new KeyEntryRecoveryData( TEST_ALIAS.getBytes(StandardCharsets.UTF_8), randomEncryptedApplicationKey(recoveryKey) ); encryptedApplicationKey(recoveryKey, applicationKeyBytes)); mRecoverableKeyStoreManager.recoverKeys( Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys( TEST_SESSION_ID, encryptedClaimResponse, ImmutableList.of(applicationKey), TEST_USER_ID); assertThat(recoveredKeys).hasSize(1); assertThat(recoveredKeys.get(TEST_ALIAS)).isEqualTo(applicationKeyBytes); } @Test Loading Loading @@ -387,9 +390,10 @@ public class RecoverableKeyStoreManagerTest { assertThat(statuses).containsEntry(alias2, status); // updated } private static byte[] randomEncryptedApplicationKey(SecretKey recoveryKey) throws Exception { private static byte[] encryptedApplicationKey( SecretKey recoveryKey, byte[] applicationKey) throws Exception { return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of( "alias", new SecretKeySpec(randomBytes(32), "AES") "alias", new SecretKeySpec(applicationKey, "AES") )).get("alias"); } Loading Loading
core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java +5 −3 Original line number Diff line number Diff line Loading @@ -423,19 +423,21 @@ public class RecoverableKeyStoreLoader { /** * Imports keys. * * @param sessionId Id for recovery session, same as in = {@link startRecoverySession}. * @param sessionId Id for recovery session, same as in * {@link #startRecoverySession(String, byte[], byte[], byte[], List)} on}. * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob * and session. KeyStore only uses package names from the application info in {@link * KeyEntryRecoveryData}. Caller is responsibility to perform certificates check. * @return Map from alias to raw key material. */ public void recoverKeys( public Map<String, byte[]> recoverKeys( @NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<KeyEntryRecoveryData> applicationKeys) throws RecoverableKeyStoreLoaderException { try { mBinder.recoverKeys( return (Map<String, byte[]>) mBinder.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys, UserHandle.getCallingUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); Loading
core/java/com/android/internal/widget/ILockSettings.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -78,6 +78,6 @@ interface ILockSettings { byte[] startRecoverySession(in String sessionId, in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge, in List<KeyStoreRecoveryMetadata> secrets, int userId); void recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, in List<KeyEntryRecoveryData> applicationKeys, int userId); }
services/core/java/com/android/server/locksettings/LockSettingsService.java +3 −3 Original line number Diff line number Diff line Loading @@ -2021,11 +2021,11 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override public void recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<KeyEntryRecoveryData> applicationKeys, @UserIdInt int userId) throws RemoteException { mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys, userId); return mRecoverableKeyStoreManager.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys, userId); } private static final String[] VALID_SETTINGS = new String[] { Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +11 −9 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; Loading Loading @@ -308,8 +309,6 @@ public class RecoverableKeyStoreManager { * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault * service. * * <p>TODO: should also load into AndroidKeyStore. * * @param sessionId The session ID used to generate the claim. See * {@link #startRecoverySession(String, byte[], byte[], byte[], List, int)}. * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault Loading @@ -317,9 +316,10 @@ public class RecoverableKeyStoreManager { * @param applicationKeys The encrypted key blobs returned by the remote vault service. These * were wrapped with the recovery key. * @param uid The uid of the recovery agent. * @return Map from alias to raw key material. * @throws RemoteException if an error occurred recovering the keys. */ public void recoverKeys( public Map<String, byte[]> recoverKeys( @NonNull String sessionId, @NonNull byte[] encryptedRecoveryKey, @NonNull List<KeyEntryRecoveryData> applicationKeys, Loading @@ -335,7 +335,7 @@ public class RecoverableKeyStoreManager { try { byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey); recoverApplicationKeys(recoveryKey, applicationKeys); return recoverApplicationKeys(recoveryKey, applicationKeys); } finally { sessionEntry.destroy(); mRecoverySessionStorage.remove(uid); Loading Loading @@ -370,20 +370,21 @@ public class RecoverableKeyStoreManager { /** * Uses {@code recoveryKey} to decrypt {@code applicationKeys}. * * <p>TODO: and load them into store? * * @return Map from alias to raw key material. * @throws RemoteException if an error occurred decrypting the keys. */ private void recoverApplicationKeys( private Map<String, byte[]> recoverApplicationKeys( @NonNull byte[] recoveryKey, @NonNull List<KeyEntryRecoveryData> applicationKeys) throws RemoteException { HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>(); for (KeyEntryRecoveryData applicationKey : applicationKeys) { String alias = new String(applicationKey.getAlias(), StandardCharsets.UTF_8); byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial(); try { // TODO: put decrypted key material in appropriate AndroidKeyStore byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial); keyMaterialByAlias.put(alias, keyMaterial); } catch (NoSuchAlgorithmException e) { // Should never happen: all the algorithms used are required by AOSP implementations throw new RemoteException( Loading @@ -399,6 +400,7 @@ public class RecoverableKeyStoreManager { /*writeableStackTrace=*/ true); } } return keyMaterialByAlias; } /** Loading
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +10 −6 Original line number Diff line number Diff line Loading @@ -285,7 +285,7 @@ public class RecoverableKeyStoreManagerTest { } @Test public void recoverKeys_doesNotThrowIfAllIsOk() throws Exception { public void recoverKeys_returnsDecryptedKeys() throws Exception { mRecoverableKeyStoreManager.startRecoverySession( TEST_SESSION_ID, TEST_PUBLIC_KEY, Loading @@ -302,16 +302,19 @@ public class RecoverableKeyStoreManagerTest { SecretKey recoveryKey = randomRecoveryKey(); byte[] encryptedClaimResponse = encryptClaimResponse( keyClaimant, TEST_SECRET, TEST_VAULT_PARAMS, recoveryKey); byte[] applicationKeyBytes = randomBytes(32); KeyEntryRecoveryData applicationKey = new KeyEntryRecoveryData( TEST_ALIAS.getBytes(StandardCharsets.UTF_8), randomEncryptedApplicationKey(recoveryKey) ); encryptedApplicationKey(recoveryKey, applicationKeyBytes)); mRecoverableKeyStoreManager.recoverKeys( Map<String, byte[]> recoveredKeys = mRecoverableKeyStoreManager.recoverKeys( TEST_SESSION_ID, encryptedClaimResponse, ImmutableList.of(applicationKey), TEST_USER_ID); assertThat(recoveredKeys).hasSize(1); assertThat(recoveredKeys.get(TEST_ALIAS)).isEqualTo(applicationKeyBytes); } @Test Loading Loading @@ -387,9 +390,10 @@ public class RecoverableKeyStoreManagerTest { assertThat(statuses).containsEntry(alias2, status); // updated } private static byte[] randomEncryptedApplicationKey(SecretKey recoveryKey) throws Exception { private static byte[] encryptedApplicationKey( SecretKey recoveryKey, byte[] applicationKey) throws Exception { return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of( "alias", new SecretKeySpec(randomBytes(32), "AES") "alias", new SecretKeySpec(applicationKey, "AES") )).get("alias"); } Loading