Loading core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java +17 −0 Original line number Diff line number Diff line Loading @@ -445,4 +445,21 @@ public class RecoverableKeyStoreLoader { throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e); } } /** * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the * raw material of the key. * * @throws RecoverableKeyStoreLoaderException if an error occurred generating and storing the * key. */ public byte[] generateAndStoreKey(String alias) throws RecoverableKeyStoreLoaderException { try { return mBinder.generateAndStoreKey(alias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e); } } } core/java/com/android/internal/widget/ILockSettings.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ interface ILockSettings { void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList, int userId); KeyStoreRecoveryData getRecoveryData(in byte[] account, int userId); byte[] generateAndStoreKey(String alias); void setSnapshotCreatedPendingIntent(in PendingIntent intent, int userId); Map getRecoverySnapshotVersions(int userId); void setServerParameters(long serverParameters, int userId); Loading services/core/java/com/android/server/locksettings/LockSettingsService.java +5 −0 Original line number Diff line number Diff line Loading @@ -2028,6 +2028,11 @@ public class LockSettingsService extends ILockSettings.Stub { sessionId, recoveryKeyBlob, applicationKeys, userId); } @Override public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException { return mRecoverableKeyStoreManager.generateAndStoreKey(alias); } private static final String[] VALID_SETTINGS = new String[] { LockPatternUtils.LOCKOUT_PERMANENT_KEY, LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java +7 −60 Original line number Diff line number Diff line Loading @@ -16,22 +16,15 @@ package com.android.server.locksettings.recoverablekeystore; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.util.Log; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; import java.security.InvalidKeyException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.Locale; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.security.auth.DestroyFailedException; /** * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form. Loading @@ -43,8 +36,6 @@ import javax.security.auth.DestroyFailedException; * @hide */ public class RecoverableKeyGenerator { private static final String TAG = "RecoverableKeyGenerator"; private static final int RESULT_CANNOT_INSERT_ROW = -1; private static final String KEY_GENERATOR_ALGORITHM = "AES"; private static final int KEY_SIZE_BITS = 256; Loading @@ -62,20 +53,16 @@ public class RecoverableKeyGenerator { // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key // material, so that it can be synced to disk in encrypted form. KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM); return new RecoverableKeyGenerator( keyGenerator, database, new AndroidKeyStoreFactory.Impl()); return new RecoverableKeyGenerator(keyGenerator, database); } private final KeyGenerator mKeyGenerator; private final RecoverableKeyStoreDb mDatabase; private final AndroidKeyStoreFactory mAndroidKeyStoreFactory; private RecoverableKeyGenerator( KeyGenerator keyGenerator, RecoverableKeyStoreDb recoverableKeyStoreDb, AndroidKeyStoreFactory androidKeyStoreFactory) { RecoverableKeyStoreDb recoverableKeyStoreDb) { mKeyGenerator = keyGenerator; mAndroidKeyStoreFactory = androidKeyStoreFactory; mDatabase = recoverableKeyStoreDb; } Loading @@ -89,69 +76,29 @@ public class RecoverableKeyGenerator { * @param platformKey The user's platform key, with which to wrap the generated key. * @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 AndroidKeyStore. * @param alias The alias by which the key will be known in the recoverable key store. * @throws RecoverableKeyStorageException if there is some error persisting the key either to * the AndroidKeyStore or the database. * the database. * @throws KeyStoreException if there is a KeyStore error wrapping the generated key. * @throws InvalidKeyException if the platform key cannot be used to wrap keys. * * @hide */ public void generateAndStoreKey( public byte[] generateAndStoreKey( PlatformEncryptionKey platformKey, int userId, int uid, String alias) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { mKeyGenerator.init(KEY_SIZE_BITS); SecretKey key = mKeyGenerator.generateKey(); KeyStoreProxy keyStore; try { keyStore = mAndroidKeyStoreFactory.getKeyStoreForUid(uid); } catch (NoSuchProviderException e) { throw new RecoverableKeyStorageException( "Impossible: AndroidKeyStore provider did not exist", e); } catch (KeyStoreException e) { throw new RecoverableKeyStorageException( "Could not load AndroidKeyStore for " + uid, e); } try { keyStore.setEntry( alias, new KeyStore.SecretKeyEntry(key), new KeyProtection.Builder( KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); } catch (KeyStoreException e) { throw new RecoverableKeyStorageException( "Failed to load (%d, %s) into AndroidKeyStore", e); } WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key); try { // Keep raw key material in memory for minimum possible time. key.destroy(); } catch (DestroyFailedException e) { Log.w(TAG, "Could not destroy SecretKey."); } long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { // Attempt to clean up try { keyStore.deleteEntry(alias); } catch (KeyStoreException e) { Log.e(TAG, String.format(Locale.US, "Could not delete recoverable key (%d, %s) from " + "AndroidKeyStore after error writing to database.", uid, alias), e); } throw new RecoverableKeyStorageException( String.format( Locale.US, "Failed writing (%d, %s) to database.", uid, alias)); } return key.getEncoded(); } } services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +49 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import java.security.InvalidKeyException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.UnrecoverableKeyException; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.HashMap; Loading @@ -59,6 +60,11 @@ import javax.crypto.AEADBadTagException; */ public class RecoverableKeyStoreManager { private static final String TAG = "RecoverableKeyStoreMgr"; private static final int ERROR_INSECURE_USER = 1; private static final int ERROR_KEYSTORE_INTERNAL_ERROR = 2; private static final int ERROR_DATABASE_ERROR = 3; private static RecoverableKeyStoreManager mInstance; private final Context mContext; Loading @@ -66,6 +72,7 @@ public class RecoverableKeyStoreManager { private final RecoverySessionStorage mRecoverySessionStorage; private final ExecutorService mExecutorService; private final ListenersStorage mListenersStorage; private final RecoverableKeyGenerator mRecoverableKeyGenerator; /** * Returns a new or existing instance. Loading Loading @@ -97,6 +104,12 @@ public class RecoverableKeyStoreManager { mRecoverySessionStorage = recoverySessionStorage; mExecutorService = executorService; mListenersStorage = listenersStorage; try { mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase); } catch (NoSuchAlgorithmException e) { // Impossible: all AOSP implementations must support AES. throw new RuntimeException(e); } } public int initRecoveryService( Loading Loading @@ -342,6 +355,42 @@ public class RecoverableKeyStoreManager { } } /** * Generates a key named {@code alias} in the recoverable store for the calling uid. Then * returns the raw key material. * * <p>TODO: Once AndroidKeyStore has added move api, do not return raw bytes. * * @hide */ public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException { int uid = Binder.getCallingUid(); int userId = Binder.getCallingUserHandle().getIdentifier(); PlatformEncryptionKey encryptionKey; try { PlatformKeyManager platformKeyManager = PlatformKeyManager.getInstance( mContext, mDatabase, userId); encryptionKey = platformKeyManager.getEncryptKey(); } catch (NoSuchAlgorithmException e) { // Impossible: all algorithms must be supported by AOSP throw new RuntimeException(e); } catch (KeyStoreException | UnrecoverableKeyException e) { throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage()); } catch (InsecureUserException e) { throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); } try { return mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); } catch (KeyStoreException | InvalidKeyException e) { throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage()); } catch (RecoverableKeyStorageException e) { throw new ServiceSpecificException(ERROR_DATABASE_ERROR, e.getMessage()); } } private byte[] decryptRecoveryKey( RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse) throws RemoteException { Loading Loading
core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java +17 −0 Original line number Diff line number Diff line Loading @@ -445,4 +445,21 @@ public class RecoverableKeyStoreLoader { throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e); } } /** * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the * raw material of the key. * * @throws RecoverableKeyStoreLoaderException if an error occurred generating and storing the * key. */ public byte[] generateAndStoreKey(String alias) throws RecoverableKeyStoreLoaderException { try { return mBinder.generateAndStoreKey(alias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e); } } }
core/java/com/android/internal/widget/ILockSettings.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ interface ILockSettings { void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList, int userId); KeyStoreRecoveryData getRecoveryData(in byte[] account, int userId); byte[] generateAndStoreKey(String alias); void setSnapshotCreatedPendingIntent(in PendingIntent intent, int userId); Map getRecoverySnapshotVersions(int userId); void setServerParameters(long serverParameters, int userId); Loading
services/core/java/com/android/server/locksettings/LockSettingsService.java +5 −0 Original line number Diff line number Diff line Loading @@ -2028,6 +2028,11 @@ public class LockSettingsService extends ILockSettings.Stub { sessionId, recoveryKeyBlob, applicationKeys, userId); } @Override public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException { return mRecoverableKeyStoreManager.generateAndStoreKey(alias); } private static final String[] VALID_SETTINGS = new String[] { LockPatternUtils.LOCKOUT_PERMANENT_KEY, LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE, Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyGenerator.java +7 −60 Original line number Diff line number Diff line Loading @@ -16,22 +16,15 @@ package com.android.server.locksettings.recoverablekeystore; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; import android.util.Log; import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb; import java.security.InvalidKeyException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.Locale; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.security.auth.DestroyFailedException; /** * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form. Loading @@ -43,8 +36,6 @@ import javax.security.auth.DestroyFailedException; * @hide */ public class RecoverableKeyGenerator { private static final String TAG = "RecoverableKeyGenerator"; private static final int RESULT_CANNOT_INSERT_ROW = -1; private static final String KEY_GENERATOR_ALGORITHM = "AES"; private static final int KEY_SIZE_BITS = 256; Loading @@ -62,20 +53,16 @@ public class RecoverableKeyGenerator { // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key // material, so that it can be synced to disk in encrypted form. KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM); return new RecoverableKeyGenerator( keyGenerator, database, new AndroidKeyStoreFactory.Impl()); return new RecoverableKeyGenerator(keyGenerator, database); } private final KeyGenerator mKeyGenerator; private final RecoverableKeyStoreDb mDatabase; private final AndroidKeyStoreFactory mAndroidKeyStoreFactory; private RecoverableKeyGenerator( KeyGenerator keyGenerator, RecoverableKeyStoreDb recoverableKeyStoreDb, AndroidKeyStoreFactory androidKeyStoreFactory) { RecoverableKeyStoreDb recoverableKeyStoreDb) { mKeyGenerator = keyGenerator; mAndroidKeyStoreFactory = androidKeyStoreFactory; mDatabase = recoverableKeyStoreDb; } Loading @@ -89,69 +76,29 @@ public class RecoverableKeyGenerator { * @param platformKey The user's platform key, with which to wrap the generated key. * @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 AndroidKeyStore. * @param alias The alias by which the key will be known in the recoverable key store. * @throws RecoverableKeyStorageException if there is some error persisting the key either to * the AndroidKeyStore or the database. * the database. * @throws KeyStoreException if there is a KeyStore error wrapping the generated key. * @throws InvalidKeyException if the platform key cannot be used to wrap keys. * * @hide */ public void generateAndStoreKey( public byte[] generateAndStoreKey( PlatformEncryptionKey platformKey, int userId, int uid, String alias) throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException { mKeyGenerator.init(KEY_SIZE_BITS); SecretKey key = mKeyGenerator.generateKey(); KeyStoreProxy keyStore; try { keyStore = mAndroidKeyStoreFactory.getKeyStoreForUid(uid); } catch (NoSuchProviderException e) { throw new RecoverableKeyStorageException( "Impossible: AndroidKeyStore provider did not exist", e); } catch (KeyStoreException e) { throw new RecoverableKeyStorageException( "Could not load AndroidKeyStore for " + uid, e); } try { keyStore.setEntry( alias, new KeyStore.SecretKeyEntry(key), new KeyProtection.Builder( KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); } catch (KeyStoreException e) { throw new RecoverableKeyStorageException( "Failed to load (%d, %s) into AndroidKeyStore", e); } WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key); try { // Keep raw key material in memory for minimum possible time. key.destroy(); } catch (DestroyFailedException e) { Log.w(TAG, "Could not destroy SecretKey."); } long result = mDatabase.insertKey(userId, uid, alias, wrappedKey); if (result == RESULT_CANNOT_INSERT_ROW) { // Attempt to clean up try { keyStore.deleteEntry(alias); } catch (KeyStoreException e) { Log.e(TAG, String.format(Locale.US, "Could not delete recoverable key (%d, %s) from " + "AndroidKeyStore after error writing to database.", uid, alias), e); } throw new RecoverableKeyStorageException( String.format( Locale.US, "Failed writing (%d, %s) to database.", uid, alias)); } return key.getEncoded(); } }
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +49 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import java.security.InvalidKeyException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.UnrecoverableKeyException; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.HashMap; Loading @@ -59,6 +60,11 @@ import javax.crypto.AEADBadTagException; */ public class RecoverableKeyStoreManager { private static final String TAG = "RecoverableKeyStoreMgr"; private static final int ERROR_INSECURE_USER = 1; private static final int ERROR_KEYSTORE_INTERNAL_ERROR = 2; private static final int ERROR_DATABASE_ERROR = 3; private static RecoverableKeyStoreManager mInstance; private final Context mContext; Loading @@ -66,6 +72,7 @@ public class RecoverableKeyStoreManager { private final RecoverySessionStorage mRecoverySessionStorage; private final ExecutorService mExecutorService; private final ListenersStorage mListenersStorage; private final RecoverableKeyGenerator mRecoverableKeyGenerator; /** * Returns a new or existing instance. Loading Loading @@ -97,6 +104,12 @@ public class RecoverableKeyStoreManager { mRecoverySessionStorage = recoverySessionStorage; mExecutorService = executorService; mListenersStorage = listenersStorage; try { mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase); } catch (NoSuchAlgorithmException e) { // Impossible: all AOSP implementations must support AES. throw new RuntimeException(e); } } public int initRecoveryService( Loading Loading @@ -342,6 +355,42 @@ public class RecoverableKeyStoreManager { } } /** * Generates a key named {@code alias} in the recoverable store for the calling uid. Then * returns the raw key material. * * <p>TODO: Once AndroidKeyStore has added move api, do not return raw bytes. * * @hide */ public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException { int uid = Binder.getCallingUid(); int userId = Binder.getCallingUserHandle().getIdentifier(); PlatformEncryptionKey encryptionKey; try { PlatformKeyManager platformKeyManager = PlatformKeyManager.getInstance( mContext, mDatabase, userId); encryptionKey = platformKeyManager.getEncryptKey(); } catch (NoSuchAlgorithmException e) { // Impossible: all algorithms must be supported by AOSP throw new RuntimeException(e); } catch (KeyStoreException | UnrecoverableKeyException e) { throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage()); } catch (InsecureUserException e) { throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); } try { return mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); } catch (KeyStoreException | InvalidKeyException e) { throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage()); } catch (RecoverableKeyStorageException e) { throw new ServiceSpecificException(ERROR_DATABASE_ERROR, e.getMessage()); } } private byte[] decryptRecoveryKey( RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse) throws RemoteException { Loading