Loading services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +13 −7 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.WrappedApplicationKey; import android.util.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; Loading Loading @@ -258,9 +259,9 @@ public class KeySyncTask implements Runnable { localLskfHash = hashCredentialsBySaltedSha256(salt, mCredential); } Map<String, SecretKey> rawKeys; Map<String, Pair<SecretKey, byte[]>> rawKeysWithMetadata; try { rawKeys = getKeysToSync(recoveryAgentUid); rawKeysWithMetadata = getKeysToSync(recoveryAgentUid); } catch (GeneralSecurityException e) { Log.e(TAG, "Failed to load recoverable keys for sync", e); return; Loading @@ -278,7 +279,9 @@ public class KeySyncTask implements Runnable { } // Only include insecure key material for test if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(rootCertAlias)) { rawKeys = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(rawKeys); rawKeysWithMetadata = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys( rawKeysWithMetadata); } SecretKey recoveryKey; Loading @@ -292,7 +295,7 @@ public class KeySyncTask implements Runnable { Map<String, byte[]> encryptedApplicationKeys; try { encryptedApplicationKeys = KeySyncUtils.encryptKeysWithRecoveryKey( recoveryKey, rawKeys); recoveryKey, rawKeysWithMetadata); } catch (InvalidKeyException | NoSuchAlgorithmException e) { Log.wtf(TAG, "Should be impossible: could not encrypt application keys with random key", Loading Loading @@ -356,7 +359,8 @@ public class KeySyncTask implements Runnable { .setCounterId(counterId) .setServerParams(vaultHandle) .setKeyChainProtectionParams(metadataList) .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys)) .setWrappedApplicationKeys( createApplicationKeyEntries(encryptedApplicationKeys, rawKeysWithMetadata)) .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey); try { keyChainSnapshotBuilder.setTrustedHardwareCertPath(certPath); Loading Loading @@ -405,7 +409,7 @@ public class KeySyncTask implements Runnable { /** * Returns all of the recoverable keys for the user. */ private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid) private Map<String, Pair<SecretKey, byte[]>> getKeysToSync(int recoveryAgentUid) throws InsecureUserException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException, InvalidKeyException, InvalidAlgorithmParameterException, IOException { Loading Loading @@ -521,12 +525,14 @@ public class KeySyncTask implements Runnable { } private static List<WrappedApplicationKey> createApplicationKeyEntries( Map<String, byte[]> encryptedApplicationKeys) { Map<String, byte[]> encryptedApplicationKeys, Map<String, Pair<SecretKey, byte[]>> originalKeysWithMetadata) { ArrayList<WrappedApplicationKey> keyEntries = new ArrayList<>(); for (String alias : encryptedApplicationKeys.keySet()) { keyEntries.add(new WrappedApplicationKey.Builder() .setAlias(alias) .setEncryptedKeyMaterial(encryptedApplicationKeys.get(alias)) .setMetadata(originalKeysWithMetadata.get(alias).second) .build()); } return keyEntries; Loading services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java +28 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.server.locksettings.recoverablekeystore; import android.annotation.Nullable; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import java.nio.ByteBuffer; Loading Loading @@ -152,15 +155,28 @@ public class KeySyncUtils { * @hide */ public static Map<String, byte[]> encryptKeysWithRecoveryKey( SecretKey recoveryKey, Map<String, SecretKey> keys) SecretKey recoveryKey, Map<String, Pair<SecretKey, byte[]>> keys) throws NoSuchAlgorithmException, InvalidKeyException { HashMap<String, byte[]> encryptedKeys = new HashMap<>(); for (String alias : keys.keySet()) { SecretKey key = keys.get(alias); SecretKey key = keys.get(alias).first; byte[] metadata = keys.get(alias).second; byte[] header; if (metadata == null) { header = ENCRYPTED_APPLICATION_KEY_HEADER; } else { // The provided metadata, if non-empty, will be bound to the authenticated // encryption process of the key material. As a result, the ciphertext cannot be // decrypted if a wrong metadata is provided during the recovery/decryption process. // Note that Android P devices do not have the API to provide the optional metadata, // so all the keys with non-empty metadata stored on Android Q+ devices cannot be // recovered on Android P devices. header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, metadata); } byte[] encryptedKey = SecureBox.encrypt( /*theirPublicKey=*/ null, /*sharedSecret=*/ recoveryKey.getEncoded(), /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER, /*header=*/ header, /*payload=*/ key.getEncoded()); encryptedKeys.put(alias, encryptedKey); } Loading Loading @@ -257,12 +273,19 @@ public class KeySyncUtils { * @throws AEADBadTagException if the message has been tampered with or was encrypted with a * different key. */ public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey) public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey, @Nullable byte[] applicationKeyMetadata) throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException { byte[] header; if (applicationKeyMetadata == null) { header = ENCRYPTED_APPLICATION_KEY_HEADER; } else { header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, applicationKeyMetadata); } return SecureBox.decrypt( /*ourPrivateKey=*/ null, /*sharedSecret=*/ recoveryKey, /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER, /*header=*/ header, /*encryptedPayload=*/ encryptedApplicationKey); } Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java +11 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.server.locksettings.recoverablekeystore; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; Loading @@ -24,7 +26,6 @@ import java.security.InvalidKeyException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.Locale; import android.util.Log; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; Loading Loading @@ -84,6 +85,8 @@ public class RecoverableKeyGenerator { * @param userId The user ID of the profile to which the calling app belongs. * @param uid The uid of the application that will own the key. * @param alias The alias by which the key will be known in the recoverable key store. * @param metadata The optional metadata that will be authenticated (but unencrypted) together * with the key material when the key is uploaded to cloud. * @throws RecoverableKeyStorageException if there is some error persisting the key either to * the database. * @throws KeyStoreException if there is a KeyStore error wrapping the generated key. Loading @@ -92,12 +95,13 @@ public class RecoverableKeyGenerator { * @hide */ public byte[] generateAndStoreKey( PlatformEncryptionKey platformKey, int userId, int uid, String alias) PlatformEncryptionKey platformKey, int userId, int uid, String alias, @Nullable byte[] metadata) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { mKeyGenerator.init(KEY_SIZE_BITS); SecretKey key = mKeyGenerator.generateKey(); WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key); WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata); long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { Loading Loading @@ -126,6 +130,8 @@ public class RecoverableKeyGenerator { * @param uid The uid of the application that will own the key. * @param alias The alias by which the key will be known in the recoverable key store. * @param keyBytes The raw bytes of the AES key to be imported. * @param metadata The optional metadata that will be authenticated (but unencrypted) together * with the key material when the key is uploaded to cloud. * @throws RecoverableKeyStorageException if there is some error persisting the key either to * the database. * @throws KeyStoreException if there is a KeyStore error wrapping the generated key. Loading @@ -135,11 +141,11 @@ public class RecoverableKeyGenerator { */ public void importKey( @NonNull PlatformEncryptionKey platformKey, int userId, int uid, @NonNull String alias, @NonNull byte[] keyBytes) @NonNull byte[] keyBytes, @Nullable byte[] metadata) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { SecretKey key = new SecretKeySpec(keyBytes, SECRET_KEY_ALGORITHM); WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key); WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata); long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +12 −13 Original line number Diff line number Diff line Loading @@ -604,7 +604,8 @@ public class RecoverableKeyStoreManager { try { byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey); Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys); Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys); return importKeyMaterials(userId, uid, keysByAlias); } catch (KeyStoreException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); Loading @@ -623,7 +624,8 @@ public class RecoverableKeyStoreManager { * @throws KeyStoreException if an error occurs importing the key or getting the grant. */ private @NonNull Map<String, String> importKeyMaterials( int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException { int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException { ArrayMap<String, String> grantAliasesByAlias = new ArrayMap<>(keysByAlias.size()); for (String alias : keysByAlias.keySet()) { mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keysByAlias.get(alias)); Loading Loading @@ -700,8 +702,6 @@ public class RecoverableKeyStoreManager { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); // TODO: Include metadata in the processes of authentication and storage PlatformEncryptionKey encryptionKey; try { encryptionKey = mPlatformKeyManager.getEncryptKey(userId); Loading @@ -715,8 +715,8 @@ public class RecoverableKeyStoreManager { } try { byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias, metadata); mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey); return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { Loading Loading @@ -768,8 +768,6 @@ public class RecoverableKeyStoreManager { + " bits."); } // TODO: Include metadata in the processes of authentication and storage int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); Loading @@ -787,7 +785,8 @@ public class RecoverableKeyStoreManager { try { // Wrap the key by the platform key and store the wrapped key locally mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes); mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes, metadata); // Import the key to Android KeyStore and get grant mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes); Loading Loading @@ -854,17 +853,17 @@ public class RecoverableKeyStoreManager { * @return Map from alias to raw key material. * @throws RemoteException if an error occurred decrypting the keys. */ private @NonNull Map<String, byte[]> recoverApplicationKeys( @NonNull byte[] recoveryKey, private @NonNull Map<String, byte[]> recoverApplicationKeys(@NonNull byte[] recoveryKey, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>(); for (WrappedApplicationKey applicationKey : applicationKeys) { String alias = applicationKey.getAlias(); byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial(); byte[] keyMetadata = applicationKey.getMetadata(); try { byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial); byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial, keyMetadata); keyMaterialByAlias.put(alias, keyMaterial); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e); Loading services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java +12 −7 Original line number Diff line number Diff line Loading @@ -18,17 +18,20 @@ package com.android.server.locksettings.recoverablekeystore; import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; import com.android.internal.widget.LockPatternUtils; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.security.keystore.recovery.TrustedRootCertificates; import android.util.Log; import android.util.Pair; import com.android.internal.widget.LockPatternUtils; import java.util.HashMap; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import javax.crypto.SecretKey; /** Loading Loading @@ -84,22 +87,24 @@ public class TestOnlyInsecureCertificateHelper { || isTestOnlyCertificateAlias(rootCertificateAlias); } public boolean doesCredentialSupportInsecureMode(int credentialType, String credential) { boolean doesCredentialSupportInsecureMode(int credentialType, String credential) { return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) && (credential != null) && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX); } public Map<String, SecretKey> keepOnlyWhitelistedInsecureKeys(Map<String, SecretKey> rawKeys) { Map<String, Pair<SecretKey, byte[]>> keepOnlyWhitelistedInsecureKeys( Map<String, Pair<SecretKey, byte[]>> rawKeys) { if (rawKeys == null) { return null; } Map<String, SecretKey> filteredKeys = new HashMap<>(); for (Map.Entry<String, SecretKey> entry : rawKeys.entrySet()) { Map<String, Pair<SecretKey, byte[]>> filteredKeys = new HashMap<>(); for (Map.Entry<String, Pair<SecretKey, byte[]>> entry : rawKeys.entrySet()) { String alias = entry.getKey(); if (alias != null && alias.startsWith(TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX)) { filteredKeys.put(entry.getKey(), entry.getValue()); filteredKeys.put(entry.getKey(), Pair.create(entry.getValue().first, entry.getValue().second)); Log.d(TAG, "adding key with insecure alias " + alias + " to the recovery snapshot"); } } Loading Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +13 −7 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.WrappedApplicationKey; import android.util.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; Loading Loading @@ -258,9 +259,9 @@ public class KeySyncTask implements Runnable { localLskfHash = hashCredentialsBySaltedSha256(salt, mCredential); } Map<String, SecretKey> rawKeys; Map<String, Pair<SecretKey, byte[]>> rawKeysWithMetadata; try { rawKeys = getKeysToSync(recoveryAgentUid); rawKeysWithMetadata = getKeysToSync(recoveryAgentUid); } catch (GeneralSecurityException e) { Log.e(TAG, "Failed to load recoverable keys for sync", e); return; Loading @@ -278,7 +279,9 @@ public class KeySyncTask implements Runnable { } // Only include insecure key material for test if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificateAlias(rootCertAlias)) { rawKeys = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(rawKeys); rawKeysWithMetadata = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys( rawKeysWithMetadata); } SecretKey recoveryKey; Loading @@ -292,7 +295,7 @@ public class KeySyncTask implements Runnable { Map<String, byte[]> encryptedApplicationKeys; try { encryptedApplicationKeys = KeySyncUtils.encryptKeysWithRecoveryKey( recoveryKey, rawKeys); recoveryKey, rawKeysWithMetadata); } catch (InvalidKeyException | NoSuchAlgorithmException e) { Log.wtf(TAG, "Should be impossible: could not encrypt application keys with random key", Loading Loading @@ -356,7 +359,8 @@ public class KeySyncTask implements Runnable { .setCounterId(counterId) .setServerParams(vaultHandle) .setKeyChainProtectionParams(metadataList) .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys)) .setWrappedApplicationKeys( createApplicationKeyEntries(encryptedApplicationKeys, rawKeysWithMetadata)) .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey); try { keyChainSnapshotBuilder.setTrustedHardwareCertPath(certPath); Loading Loading @@ -405,7 +409,7 @@ public class KeySyncTask implements Runnable { /** * Returns all of the recoverable keys for the user. */ private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid) private Map<String, Pair<SecretKey, byte[]>> getKeysToSync(int recoveryAgentUid) throws InsecureUserException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException, InvalidKeyException, InvalidAlgorithmParameterException, IOException { Loading Loading @@ -521,12 +525,14 @@ public class KeySyncTask implements Runnable { } private static List<WrappedApplicationKey> createApplicationKeyEntries( Map<String, byte[]> encryptedApplicationKeys) { Map<String, byte[]> encryptedApplicationKeys, Map<String, Pair<SecretKey, byte[]>> originalKeysWithMetadata) { ArrayList<WrappedApplicationKey> keyEntries = new ArrayList<>(); for (String alias : encryptedApplicationKeys.keySet()) { keyEntries.add(new WrappedApplicationKey.Builder() .setAlias(alias) .setEncryptedKeyMaterial(encryptedApplicationKeys.get(alias)) .setMetadata(originalKeysWithMetadata.get(alias).second) .build()); } return keyEntries; Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java +28 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.server.locksettings.recoverablekeystore; import android.annotation.Nullable; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import java.nio.ByteBuffer; Loading Loading @@ -152,15 +155,28 @@ public class KeySyncUtils { * @hide */ public static Map<String, byte[]> encryptKeysWithRecoveryKey( SecretKey recoveryKey, Map<String, SecretKey> keys) SecretKey recoveryKey, Map<String, Pair<SecretKey, byte[]>> keys) throws NoSuchAlgorithmException, InvalidKeyException { HashMap<String, byte[]> encryptedKeys = new HashMap<>(); for (String alias : keys.keySet()) { SecretKey key = keys.get(alias); SecretKey key = keys.get(alias).first; byte[] metadata = keys.get(alias).second; byte[] header; if (metadata == null) { header = ENCRYPTED_APPLICATION_KEY_HEADER; } else { // The provided metadata, if non-empty, will be bound to the authenticated // encryption process of the key material. As a result, the ciphertext cannot be // decrypted if a wrong metadata is provided during the recovery/decryption process. // Note that Android P devices do not have the API to provide the optional metadata, // so all the keys with non-empty metadata stored on Android Q+ devices cannot be // recovered on Android P devices. header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, metadata); } byte[] encryptedKey = SecureBox.encrypt( /*theirPublicKey=*/ null, /*sharedSecret=*/ recoveryKey.getEncoded(), /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER, /*header=*/ header, /*payload=*/ key.getEncoded()); encryptedKeys.put(alias, encryptedKey); } Loading Loading @@ -257,12 +273,19 @@ public class KeySyncUtils { * @throws AEADBadTagException if the message has been tampered with or was encrypted with a * different key. */ public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey) public static byte[] decryptApplicationKey(byte[] recoveryKey, byte[] encryptedApplicationKey, @Nullable byte[] applicationKeyMetadata) throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException { byte[] header; if (applicationKeyMetadata == null) { header = ENCRYPTED_APPLICATION_KEY_HEADER; } else { header = concat(ENCRYPTED_APPLICATION_KEY_HEADER, applicationKeyMetadata); } return SecureBox.decrypt( /*ourPrivateKey=*/ null, /*sharedSecret=*/ recoveryKey, /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER, /*header=*/ header, /*encryptedPayload=*/ encryptedApplicationKey); } Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java +11 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.server.locksettings.recoverablekeystore; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; Loading @@ -24,7 +26,6 @@ import java.security.InvalidKeyException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.Locale; import android.util.Log; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; Loading Loading @@ -84,6 +85,8 @@ public class RecoverableKeyGenerator { * @param userId The user ID of the profile to which the calling app belongs. * @param uid The uid of the application that will own the key. * @param alias The alias by which the key will be known in the recoverable key store. * @param metadata The optional metadata that will be authenticated (but unencrypted) together * with the key material when the key is uploaded to cloud. * @throws RecoverableKeyStorageException if there is some error persisting the key either to * the database. * @throws KeyStoreException if there is a KeyStore error wrapping the generated key. Loading @@ -92,12 +95,13 @@ public class RecoverableKeyGenerator { * @hide */ public byte[] generateAndStoreKey( PlatformEncryptionKey platformKey, int userId, int uid, String alias) PlatformEncryptionKey platformKey, int userId, int uid, String alias, @Nullable byte[] metadata) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { mKeyGenerator.init(KEY_SIZE_BITS); SecretKey key = mKeyGenerator.generateKey(); WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key); WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata); long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { Loading Loading @@ -126,6 +130,8 @@ public class RecoverableKeyGenerator { * @param uid The uid of the application that will own the key. * @param alias The alias by which the key will be known in the recoverable key store. * @param keyBytes The raw bytes of the AES key to be imported. * @param metadata The optional metadata that will be authenticated (but unencrypted) together * with the key material when the key is uploaded to cloud. * @throws RecoverableKeyStorageException if there is some error persisting the key either to * the database. * @throws KeyStoreException if there is a KeyStore error wrapping the generated key. Loading @@ -135,11 +141,11 @@ public class RecoverableKeyGenerator { */ public void importKey( @NonNull PlatformEncryptionKey platformKey, int userId, int uid, @NonNull String alias, @NonNull byte[] keyBytes) @NonNull byte[] keyBytes, @Nullable byte[] metadata) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { SecretKey key = new SecretKeySpec(keyBytes, SECRET_KEY_ALGORITHM); WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key); WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key, metadata); long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +12 −13 Original line number Diff line number Diff line Loading @@ -604,7 +604,8 @@ public class RecoverableKeyStoreManager { try { byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey); Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys); Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys); return importKeyMaterials(userId, uid, keysByAlias); } catch (KeyStoreException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); Loading @@ -623,7 +624,8 @@ public class RecoverableKeyStoreManager { * @throws KeyStoreException if an error occurs importing the key or getting the grant. */ private @NonNull Map<String, String> importKeyMaterials( int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException { int userId, int uid, Map<String, byte[]> keysByAlias) throws KeyStoreException { ArrayMap<String, String> grantAliasesByAlias = new ArrayMap<>(keysByAlias.size()); for (String alias : keysByAlias.keySet()) { mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keysByAlias.get(alias)); Loading Loading @@ -700,8 +702,6 @@ public class RecoverableKeyStoreManager { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); // TODO: Include metadata in the processes of authentication and storage PlatformEncryptionKey encryptionKey; try { encryptionKey = mPlatformKeyManager.getEncryptKey(userId); Loading @@ -715,8 +715,8 @@ public class RecoverableKeyStoreManager { } try { byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias, metadata); mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey); return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { Loading Loading @@ -768,8 +768,6 @@ public class RecoverableKeyStoreManager { + " bits."); } // TODO: Include metadata in the processes of authentication and storage int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); Loading @@ -787,7 +785,8 @@ public class RecoverableKeyStoreManager { try { // Wrap the key by the platform key and store the wrapped key locally mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes); mRecoverableKeyGenerator.importKey(encryptionKey, userId, uid, alias, keyBytes, metadata); // Import the key to Android KeyStore and get grant mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes); Loading Loading @@ -854,17 +853,17 @@ public class RecoverableKeyStoreManager { * @return Map from alias to raw key material. * @throws RemoteException if an error occurred decrypting the keys. */ private @NonNull Map<String, byte[]> recoverApplicationKeys( @NonNull byte[] recoveryKey, private @NonNull Map<String, byte[]> recoverApplicationKeys(@NonNull byte[] recoveryKey, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { HashMap<String, byte[]> keyMaterialByAlias = new HashMap<>(); for (WrappedApplicationKey applicationKey : applicationKeys) { String alias = applicationKey.getAlias(); byte[] encryptedKeyMaterial = applicationKey.getEncryptedKeyMaterial(); byte[] keyMetadata = applicationKey.getMetadata(); try { byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial); byte[] keyMaterial = KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial, keyMetadata); keyMaterialByAlias.put(alias, keyMaterial); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e); Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java +12 −7 Original line number Diff line number Diff line Loading @@ -18,17 +18,20 @@ package com.android.server.locksettings.recoverablekeystore; import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; import com.android.internal.widget.LockPatternUtils; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.security.keystore.recovery.TrustedRootCertificates; import android.util.Log; import android.util.Pair; import com.android.internal.widget.LockPatternUtils; import java.util.HashMap; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import javax.crypto.SecretKey; /** Loading Loading @@ -84,22 +87,24 @@ public class TestOnlyInsecureCertificateHelper { || isTestOnlyCertificateAlias(rootCertificateAlias); } public boolean doesCredentialSupportInsecureMode(int credentialType, String credential) { boolean doesCredentialSupportInsecureMode(int credentialType, String credential) { return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) && (credential != null) && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX); } public Map<String, SecretKey> keepOnlyWhitelistedInsecureKeys(Map<String, SecretKey> rawKeys) { Map<String, Pair<SecretKey, byte[]>> keepOnlyWhitelistedInsecureKeys( Map<String, Pair<SecretKey, byte[]>> rawKeys) { if (rawKeys == null) { return null; } Map<String, SecretKey> filteredKeys = new HashMap<>(); for (Map.Entry<String, SecretKey> entry : rawKeys.entrySet()) { Map<String, Pair<SecretKey, byte[]>> filteredKeys = new HashMap<>(); for (Map.Entry<String, Pair<SecretKey, byte[]>> entry : rawKeys.entrySet()) { String alias = entry.getKey(); if (alias != null && alias.startsWith(TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX)) { filteredKeys.put(entry.getKey(), entry.getValue()); filteredKeys.put(entry.getKey(), Pair.create(entry.getValue().first, entry.getValue().second)); Log.d(TAG, "adding key with insecure alias " + alias + " to the recovery snapshot"); } } Loading