diff --git a/core/java/android/security/keystore/KeychainSnapshot.java b/core/java/android/security/keystore/KeychainSnapshot.java index 2029114f90606..e03dd4a62ca0e 100644 --- a/core/java/android/security/keystore/KeychainSnapshot.java +++ b/core/java/android/security/keystore/KeychainSnapshot.java @@ -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 mKeychainProtectionParams; private List mEntryRecoveryData; private byte[] mEncryptedRecoveryKeyBlob; @@ -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. */ @@ -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 * @@ -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; } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java index a29e1be0fdcae..a48304db396aa 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -263,12 +263,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); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index c6fc675db6b15..42f4b352642aa 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -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); @@ -304,6 +308,11 @@ public class KeySyncTaskTest { TEST_VAULT_HANDLE)); List 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());