Loading core/java/android/security/keystore/KeychainSnapshot.java +84 −0 Original line number Diff line number Diff line Loading @@ -43,7 +43,14 @@ import java.util.List; * @hide */ public final class KeychainSnapshot implements Parcelable { private static final int DEFAULT_MAX_ATTEMPTS = 10; private static final long DEFAULT_COUNTER_ID = 1L; private int mSnapshotVersion; private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS; private long mCounterId = DEFAULT_COUNTER_ID; private byte[] mServerParams; private byte[] mPublicKey; private List<KeychainProtectionParams> mKeychainProtectionParams; private List<WrappedApplicationKey> mEntryRecoveryData; private byte[] mEncryptedRecoveryKeyBlob; Loading Loading @@ -78,6 +85,37 @@ public final class KeychainSnapshot implements Parcelable { return mSnapshotVersion; } /** * Number of user secret guesses allowed during Keychain recovery. */ public int getMaxAttempts() { return mMaxAttempts; } /** * CounterId which is rotated together with user secret. */ public long getCounterId() { return mCounterId; } /** * Server parameters. */ public @NonNull byte[] getServerParams() { return mServerParams; } /** * Public key used to encrypt {@code encryptedRecoveryKeyBlob}. * * See implementation for binary key format */ // TODO: document key format. public @NonNull byte[] getTrustedHardwarePublicKey() { return mPublicKey; } /** * UI and key derivation parameters. Note that combination of secrets may be used. */ Loading Loading @@ -128,6 +166,50 @@ public final class KeychainSnapshot implements Parcelable { return this; } /** * Sets the number of user secret guesses allowed during Keychain recovery. * * @param maxAttempts The maximum number of guesses. * @return This builder. */ public Builder setMaxAttempts(int maxAttempts) { mInstance.mMaxAttempts = maxAttempts; return this; } /** * Sets counter id. * * @param counterId The counter id. * @return This builder. */ public Builder setCounterId(long counterId) { mInstance.mCounterId = counterId; return this; } /** * Sets server parameters. * * @param serverParams The server parameters * @return This builder. */ public Builder setServerParams(byte[] serverParams) { mInstance.mServerParams = serverParams; return this; } /** * Sets public key used to encrypt recovery blob. * * @param publicKey The public key * @return This builder. */ public Builder setTrustedHardwarePublicKey(byte[] publicKey) { mInstance.mPublicKey = publicKey; return this; } /** * Sets UI and key derivation parameters * Loading Loading @@ -175,6 +257,8 @@ public final class KeychainSnapshot implements Parcelable { Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData, "entryRecoveryData"); Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob); Preconditions.checkNotNull(mInstance.mServerParams); Preconditions.checkNotNull(mInstance.mPublicKey); return mInstance; } } Loading services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +10 −6 Original line number Diff line number Diff line Loading @@ -267,12 +267,16 @@ public class KeySyncTask implements Runnable { // If application keys are not updated, snapshot will not be created on next unlock. mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, false); // TODO: use Builder. mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot( snapshotVersion, /*recoveryMetadata=*/ metadataList, /*applicationKeyBlobs=*/ createApplicationKeyEntries(encryptedApplicationKeys), /*encryptedRecoveryKeyblob=*/ encryptedRecoveryKey)); mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot.Builder() .setSnapshotVersion(snapshotVersion) .setMaxAttempts(TRUSTED_HARDWARE_MAX_ATTEMPTS) .setCounterId(counterId) .setTrustedHardwarePublicKey(SecureBox.encodePublicKey(publicKey)) .setServerParams(vaultHandle) .setKeychainProtectionParams(metadataList) .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys)) .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey) .build()); mSnapshotListenersStorage.recoverySnapshotAvailable(recoveryAgentUid); } Loading services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +9 −0 Original line number Diff line number Diff line Loading @@ -278,9 +278,13 @@ public class KeySyncTaskTest { public void run_sendsEncryptedKeysIfAvailableToSync() throws Exception { mRecoverableKeyStoreDb.setRecoveryServicePublicKey( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic()); mRecoverableKeyStoreDb.setServerParams( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); SecretKey applicationKey = addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); mKeySyncTask.run(); KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); Loading @@ -305,6 +309,11 @@ public class KeySyncTaskTest { TEST_VAULT_HANDLE)); List<WrappedApplicationKey> applicationKeys = keychainSnapshot.getWrappedApplicationKeys(); assertThat(applicationKeys).hasSize(1); assertThat(keychainSnapshot.getCounterId()).isEqualTo(counterId); assertThat(keychainSnapshot.getMaxAttempts()).isEqualTo(10); assertThat(keychainSnapshot.getTrustedHardwarePublicKey()) .isEqualTo(SecureBox.encodePublicKey(mKeyPair.getPublic())); assertThat(keychainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE); WrappedApplicationKey keyData = applicationKeys.get(0); assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias()); assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias()); Loading Loading
core/java/android/security/keystore/KeychainSnapshot.java +84 −0 Original line number Diff line number Diff line Loading @@ -43,7 +43,14 @@ import java.util.List; * @hide */ public final class KeychainSnapshot implements Parcelable { private static final int DEFAULT_MAX_ATTEMPTS = 10; private static final long DEFAULT_COUNTER_ID = 1L; private int mSnapshotVersion; private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS; private long mCounterId = DEFAULT_COUNTER_ID; private byte[] mServerParams; private byte[] mPublicKey; private List<KeychainProtectionParams> mKeychainProtectionParams; private List<WrappedApplicationKey> mEntryRecoveryData; private byte[] mEncryptedRecoveryKeyBlob; Loading Loading @@ -78,6 +85,37 @@ public final class KeychainSnapshot implements Parcelable { return mSnapshotVersion; } /** * Number of user secret guesses allowed during Keychain recovery. */ public int getMaxAttempts() { return mMaxAttempts; } /** * CounterId which is rotated together with user secret. */ public long getCounterId() { return mCounterId; } /** * Server parameters. */ public @NonNull byte[] getServerParams() { return mServerParams; } /** * Public key used to encrypt {@code encryptedRecoveryKeyBlob}. * * See implementation for binary key format */ // TODO: document key format. public @NonNull byte[] getTrustedHardwarePublicKey() { return mPublicKey; } /** * UI and key derivation parameters. Note that combination of secrets may be used. */ Loading Loading @@ -128,6 +166,50 @@ public final class KeychainSnapshot implements Parcelable { return this; } /** * Sets the number of user secret guesses allowed during Keychain recovery. * * @param maxAttempts The maximum number of guesses. * @return This builder. */ public Builder setMaxAttempts(int maxAttempts) { mInstance.mMaxAttempts = maxAttempts; return this; } /** * Sets counter id. * * @param counterId The counter id. * @return This builder. */ public Builder setCounterId(long counterId) { mInstance.mCounterId = counterId; return this; } /** * Sets server parameters. * * @param serverParams The server parameters * @return This builder. */ public Builder setServerParams(byte[] serverParams) { mInstance.mServerParams = serverParams; return this; } /** * Sets public key used to encrypt recovery blob. * * @param publicKey The public key * @return This builder. */ public Builder setTrustedHardwarePublicKey(byte[] publicKey) { mInstance.mPublicKey = publicKey; return this; } /** * Sets UI and key derivation parameters * Loading Loading @@ -175,6 +257,8 @@ public final class KeychainSnapshot implements Parcelable { Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData, "entryRecoveryData"); Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob); Preconditions.checkNotNull(mInstance.mServerParams); Preconditions.checkNotNull(mInstance.mPublicKey); return mInstance; } } Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +10 −6 Original line number Diff line number Diff line Loading @@ -267,12 +267,16 @@ public class KeySyncTask implements Runnable { // If application keys are not updated, snapshot will not be created on next unlock. mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, false); // TODO: use Builder. mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot( snapshotVersion, /*recoveryMetadata=*/ metadataList, /*applicationKeyBlobs=*/ createApplicationKeyEntries(encryptedApplicationKeys), /*encryptedRecoveryKeyblob=*/ encryptedRecoveryKey)); mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot.Builder() .setSnapshotVersion(snapshotVersion) .setMaxAttempts(TRUSTED_HARDWARE_MAX_ATTEMPTS) .setCounterId(counterId) .setTrustedHardwarePublicKey(SecureBox.encodePublicKey(publicKey)) .setServerParams(vaultHandle) .setKeychainProtectionParams(metadataList) .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys)) .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey) .build()); mSnapshotListenersStorage.recoverySnapshotAvailable(recoveryAgentUid); } Loading
services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +9 −0 Original line number Diff line number Diff line Loading @@ -278,9 +278,13 @@ public class KeySyncTaskTest { public void run_sendsEncryptedKeysIfAvailableToSync() throws Exception { mRecoverableKeyStoreDb.setRecoveryServicePublicKey( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic()); mRecoverableKeyStoreDb.setServerParams( TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE); when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true); SecretKey applicationKey = addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS); mKeySyncTask.run(); KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID); Loading @@ -305,6 +309,11 @@ public class KeySyncTaskTest { TEST_VAULT_HANDLE)); List<WrappedApplicationKey> applicationKeys = keychainSnapshot.getWrappedApplicationKeys(); assertThat(applicationKeys).hasSize(1); assertThat(keychainSnapshot.getCounterId()).isEqualTo(counterId); assertThat(keychainSnapshot.getMaxAttempts()).isEqualTo(10); assertThat(keychainSnapshot.getTrustedHardwarePublicKey()) .isEqualTo(SecureBox.encodePublicKey(mKeyPair.getPublic())); assertThat(keychainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE); WrappedApplicationKey keyData = applicationKeys.get(0); assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias()); assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias()); Loading