Loading core/java/android/security/keystore/recovery/RecoveryController.java +13 −12 Original line number Diff line number Diff line Loading @@ -547,10 +547,7 @@ public class RecoveryController { if (grantAlias == null) { throw new InternalRecoveryServiceException("null grant alias"); } return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( mKeyStore, grantAlias, KeyStore.UID_SELF); return getKeyFromGrant(grantAlias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (UnrecoverableKeyException e) { Loading Loading @@ -581,10 +578,7 @@ public class RecoveryController { if (grantAlias == null) { throw new InternalRecoveryServiceException("Null grant alias"); } return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( mKeyStore, grantAlias, KeyStore.UID_SELF); return getKeyFromGrant(alias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (UnrecoverableKeyException e) { Loading Loading @@ -614,10 +608,7 @@ public class RecoveryController { if (grantAlias == null) { return null; } return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( mKeyStore, grantAlias, KeyStore.UID_SELF); return getKeyFromGrant(grantAlias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { Loading @@ -625,6 +616,16 @@ public class RecoveryController { } } /** * Returns the key with the given {@code grantAlias}. */ Key getKeyFromGrant(String grantAlias) throws UnrecoverableKeyException { return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( mKeyStore, grantAlias, KeyStore.UID_SELF); } /** * Removes a key called {@code alias} from the recoverable key store. * Loading core/java/android/security/keystore/recovery/RecoverySession.java +62 −0 Original line number Diff line number Diff line Loading @@ -16,17 +16,22 @@ package android.security.keystore.recovery; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.util.ArrayMap; import android.util.Log; import java.security.Key; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertPath; import java.security.cert.CertificateException; import java.util.List; import java.util.Locale; import java.util.Map; /** Loading Loading @@ -243,6 +248,63 @@ public class RecoverySession implements AutoCloseable { } } /** * Imports key chain snapshot recovered from a remote vault. * * @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. * @throws SessionExpiredException if {@code session} has since been closed. * @throws DecryptionFailedException if unable to decrypt the snapshot. * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. * * @hide */ @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE) public Map<String, Key> recoverKeyChainSnapshot( @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException { try { Map<String, String> grantAliases = mRecoveryController .getBinder() .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys); return getKeysFromGrants(grantAliases); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) { throw new DecryptionFailedException(e.getMessage()); } if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) { throw new SessionExpiredException(e.getMessage()); } throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); } } /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */ private Map<String, Key> getKeysFromGrants(Map<String, String> grantAliases) throws InternalRecoveryServiceException { ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size()); for (String alias : grantAliases.keySet()) { String grantAlias = grantAliases.get(alias); Key key; try { key = mRecoveryController.getKeyFromGrant(grantAlias); } catch (UnrecoverableKeyException e) { throw new InternalRecoveryServiceException( String.format( Locale.US, "Failed to get key '%s' from grant '%s'", alias, grantAlias), e); } keysByAlias.put(alias, key); } return keysByAlias; } /** * An internal session ID, used by the framework to match recovery claims to snapshot responses. * Loading core/java/com/android/internal/widget/ILockSettings.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -83,5 +83,9 @@ interface ILockSettings { in List<KeyChainProtectionParams> secrets); Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, in List<WrappedApplicationKey> applicationKeys); Map/*<String, String>*/ recoverKeyChainSnapshot( in String sessionId, in byte[] recoveryKeyBlob, in List<WrappedApplicationKey> applicationKeys); void closeSession(in String sessionId); } services/core/java/com/android/server/locksettings/LockSettingsService.java +10 −2 Original line number Diff line number Diff line Loading @@ -2064,12 +2064,20 @@ public class LockSettingsService extends ILockSettings.Stub { mRecoverableKeyStoreManager.closeSession(sessionId); } @Override public Map<String, String> recoverKeyChainSnapshot( @NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { return mRecoverableKeyStoreManager.recoverKeyChainSnapshot( sessionId, recoveryKeyBlob, applicationKeys); } @Override public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { return mRecoverableKeyStoreManager.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys); return mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys); } @Override Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +77 −4 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Context; import android.os.Binder; import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; Loading @@ -41,6 +42,7 @@ import android.security.keystore.recovery.RecoveryController; import android.security.keystore.recovery.TrustedRootCertificates; import android.security.keystore.recovery.WrappedApplicationKey; import android.security.KeyStore; import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -555,6 +557,78 @@ public class RecoverableKeyStoreManager { } } /** * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault * service. * * @param sessionId The session ID used to generate the claim. See * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}. * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault * service. * @param applicationKeys The encrypted key blobs returned by the remote vault service. These * were wrapped with the recovery key. * @throws RemoteException if an error occurred recovering the keys. */ public Map<String, String> recoverKeyChainSnapshot( @NonNull String sessionId, @NonNull byte[] encryptedRecoveryKey, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { checkRecoverKeyStorePermission(); int userId = UserHandle.getCallingUserId(); int uid = Binder.getCallingUid(); RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId); if (sessionEntry == null) { throw new ServiceSpecificException(ERROR_SESSION_EXPIRED, String.format(Locale.US, "Application uid=%d does not have pending session '%s'", uid, sessionId)); } try { byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey); Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys); return importKeyMaterials(userId, uid, keysByAlias); } catch (KeyStoreException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } finally { sessionEntry.destroy(); mRecoverySessionStorage.remove(uid); } } /** * Imports the key materials, returning a map from alias to grant alias for the calling user. * * @param userId The calling user ID. * @param uid The calling uid. * @param keysByAlias The key materials, keyed by alias. * @throws KeyStoreException if an error occurs importing the key or getting the grant. */ private Map<String, String> importKeyMaterials( 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)); String grantAlias = getAlias(userId, uid, alias); Log.i(TAG, String.format(Locale.US, "Import %s -> %s", alias, grantAlias)); grantAliasesByAlias.put(alias, grantAlias); } return grantAliasesByAlias; } /** * Returns an alias for the key. * * @param userId The user ID of the calling process. * @param uid The uid of the calling process. * @param alias The alias of the key. * @return The alias in the calling process's keystore. */ private String getAlias(int userId, int uid, String alias) { return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); } /** * Deprecated * Generates a key named {@code alias} in the recoverable store for the calling uid. Then Loading Loading @@ -632,7 +706,7 @@ public class RecoverableKeyStoreManager { byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey); return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } Loading Loading @@ -683,7 +757,7 @@ public class RecoverableKeyStoreManager { // Import the key to Android KeyStore and get grant mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes); return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } Loading @@ -697,8 +771,7 @@ public class RecoverableKeyStoreManager { public String getKey(@NonNull String alias) throws RemoteException { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias); return grantAlias; return getAlias(userId, uid, alias); } private byte[] decryptRecoveryKey( Loading Loading
core/java/android/security/keystore/recovery/RecoveryController.java +13 −12 Original line number Diff line number Diff line Loading @@ -547,10 +547,7 @@ public class RecoveryController { if (grantAlias == null) { throw new InternalRecoveryServiceException("null grant alias"); } return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( mKeyStore, grantAlias, KeyStore.UID_SELF); return getKeyFromGrant(grantAlias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (UnrecoverableKeyException e) { Loading Loading @@ -581,10 +578,7 @@ public class RecoveryController { if (grantAlias == null) { throw new InternalRecoveryServiceException("Null grant alias"); } return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( mKeyStore, grantAlias, KeyStore.UID_SELF); return getKeyFromGrant(alias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (UnrecoverableKeyException e) { Loading Loading @@ -614,10 +608,7 @@ public class RecoveryController { if (grantAlias == null) { return null; } return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( mKeyStore, grantAlias, KeyStore.UID_SELF); return getKeyFromGrant(grantAlias); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { Loading @@ -625,6 +616,16 @@ public class RecoveryController { } } /** * Returns the key with the given {@code grantAlias}. */ Key getKeyFromGrant(String grantAlias) throws UnrecoverableKeyException { return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( mKeyStore, grantAlias, KeyStore.UID_SELF); } /** * Removes a key called {@code alias} from the recoverable key store. * Loading
core/java/android/security/keystore/recovery/RecoverySession.java +62 −0 Original line number Diff line number Diff line Loading @@ -16,17 +16,22 @@ package android.security.keystore.recovery; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.util.ArrayMap; import android.util.Log; import java.security.Key; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertPath; import java.security.cert.CertificateException; import java.util.List; import java.util.Locale; import java.util.Map; /** Loading Loading @@ -243,6 +248,63 @@ public class RecoverySession implements AutoCloseable { } } /** * Imports key chain snapshot recovered from a remote vault. * * @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. * @throws SessionExpiredException if {@code session} has since been closed. * @throws DecryptionFailedException if unable to decrypt the snapshot. * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. * * @hide */ @RequiresPermission(Manifest.permission.RECOVER_KEYSTORE) public Map<String, Key> recoverKeyChainSnapshot( @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys ) throws SessionExpiredException, DecryptionFailedException, InternalRecoveryServiceException { try { Map<String, String> grantAliases = mRecoveryController .getBinder() .recoverKeyChainSnapshot(mSessionId, recoveryKeyBlob, applicationKeys); return getKeysFromGrants(grantAliases); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) { throw new DecryptionFailedException(e.getMessage()); } if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) { throw new SessionExpiredException(e.getMessage()); } throw mRecoveryController.wrapUnexpectedServiceSpecificException(e); } } /** Given a map from alias to grant alias, returns a map from alias to a {@link Key} handle. */ private Map<String, Key> getKeysFromGrants(Map<String, String> grantAliases) throws InternalRecoveryServiceException { ArrayMap<String, Key> keysByAlias = new ArrayMap<>(grantAliases.size()); for (String alias : grantAliases.keySet()) { String grantAlias = grantAliases.get(alias); Key key; try { key = mRecoveryController.getKeyFromGrant(grantAlias); } catch (UnrecoverableKeyException e) { throw new InternalRecoveryServiceException( String.format( Locale.US, "Failed to get key '%s' from grant '%s'", alias, grantAlias), e); } keysByAlias.put(alias, key); } return keysByAlias; } /** * An internal session ID, used by the framework to match recovery claims to snapshot responses. * Loading
core/java/com/android/internal/widget/ILockSettings.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -83,5 +83,9 @@ interface ILockSettings { in List<KeyChainProtectionParams> secrets); Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob, in List<WrappedApplicationKey> applicationKeys); Map/*<String, String>*/ recoverKeyChainSnapshot( in String sessionId, in byte[] recoveryKeyBlob, in List<WrappedApplicationKey> applicationKeys); void closeSession(in String sessionId); }
services/core/java/com/android/server/locksettings/LockSettingsService.java +10 −2 Original line number Diff line number Diff line Loading @@ -2064,12 +2064,20 @@ public class LockSettingsService extends ILockSettings.Stub { mRecoverableKeyStoreManager.closeSession(sessionId); } @Override public Map<String, String> recoverKeyChainSnapshot( @NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { return mRecoverableKeyStoreManager.recoverKeyChainSnapshot( sessionId, recoveryKeyBlob, applicationKeys); } @Override public Map<String, byte[]> recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { return mRecoverableKeyStoreManager.recoverKeys( sessionId, recoveryKeyBlob, applicationKeys); return mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys); } @Override Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +77 −4 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Context; import android.os.Binder; import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; Loading @@ -41,6 +42,7 @@ import android.security.keystore.recovery.RecoveryController; import android.security.keystore.recovery.TrustedRootCertificates; import android.security.keystore.recovery.WrappedApplicationKey; import android.security.KeyStore; import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -555,6 +557,78 @@ public class RecoverableKeyStoreManager { } } /** * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault * service. * * @param sessionId The session ID used to generate the claim. See * {@link #startRecoverySession(String, byte[], byte[], byte[], List)}. * @param encryptedRecoveryKey The encrypted recovery key blob returned by the remote vault * service. * @param applicationKeys The encrypted key blobs returned by the remote vault service. These * were wrapped with the recovery key. * @throws RemoteException if an error occurred recovering the keys. */ public Map<String, String> recoverKeyChainSnapshot( @NonNull String sessionId, @NonNull byte[] encryptedRecoveryKey, @NonNull List<WrappedApplicationKey> applicationKeys) throws RemoteException { checkRecoverKeyStorePermission(); int userId = UserHandle.getCallingUserId(); int uid = Binder.getCallingUid(); RecoverySessionStorage.Entry sessionEntry = mRecoverySessionStorage.get(uid, sessionId); if (sessionEntry == null) { throw new ServiceSpecificException(ERROR_SESSION_EXPIRED, String.format(Locale.US, "Application uid=%d does not have pending session '%s'", uid, sessionId)); } try { byte[] recoveryKey = decryptRecoveryKey(sessionEntry, encryptedRecoveryKey); Map<String, byte[]> keysByAlias = recoverApplicationKeys(recoveryKey, applicationKeys); return importKeyMaterials(userId, uid, keysByAlias); } catch (KeyStoreException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } finally { sessionEntry.destroy(); mRecoverySessionStorage.remove(uid); } } /** * Imports the key materials, returning a map from alias to grant alias for the calling user. * * @param userId The calling user ID. * @param uid The calling uid. * @param keysByAlias The key materials, keyed by alias. * @throws KeyStoreException if an error occurs importing the key or getting the grant. */ private Map<String, String> importKeyMaterials( 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)); String grantAlias = getAlias(userId, uid, alias); Log.i(TAG, String.format(Locale.US, "Import %s -> %s", alias, grantAlias)); grantAliasesByAlias.put(alias, grantAlias); } return grantAliasesByAlias; } /** * Returns an alias for the key. * * @param userId The user ID of the calling process. * @param uid The uid of the calling process. * @param alias The alias of the key. * @return The alias in the calling process's keystore. */ private String getAlias(int userId, int uid, String alias) { return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); } /** * Deprecated * Generates a key named {@code alias} in the recoverable store for the calling uid. Then Loading Loading @@ -632,7 +706,7 @@ public class RecoverableKeyStoreManager { byte[] secretKey = mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, secretKey); return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } Loading Loading @@ -683,7 +757,7 @@ public class RecoverableKeyStoreManager { // Import the key to Android KeyStore and get grant mApplicationKeyStorage.setSymmetricKeyEntry(userId, uid, alias, keyBytes); return mApplicationKeyStorage.getGrantAlias(userId, uid, alias); return getAlias(userId, uid, alias); } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); } Loading @@ -697,8 +771,7 @@ public class RecoverableKeyStoreManager { public String getKey(@NonNull String alias) throws RemoteException { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); String grantAlias = mApplicationKeyStorage.getGrantAlias(userId, uid, alias); return grantAlias; return getAlias(userId, uid, alias); } private byte[] decryptRecoveryKey( Loading