Loading core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java +51 −22 Original line number Diff line number Diff line Loading @@ -43,18 +43,61 @@ public class RecoverableKeyStoreLoader { public static final int NO_ERROR = KeyStore.NO_ERROR; public static final int SYSTEM_ERROR = KeyStore.SYSTEM_ERROR; /** * Failed because the loader has not been initialized with a recovery public key yet. */ public static final int ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20; /** * Failed because no snapshot is yet pending to be synced for the user. */ public static final int ERROR_NO_SNAPSHOT_PENDING = 21; /** * Failed due to an error internal to AndroidKeyStore. */ public static final int ERROR_KEYSTORE_INTERNAL_ERROR = 22; /** * Failed because the user does not have a lock screen set. */ public static final int ERROR_INSECURE_USER = 24; /** * Failed because of an internal database error. */ public static final int ERROR_DATABASE_ERROR = 25; /** * Failed because the provided certificate was not a valid X509 certificate. */ public static final int ERROR_BAD_X509_CERTIFICATE = 26; /** * Should never be thrown - some algorithm that all AOSP implementations must support is * not available. */ public static final int ERROR_UNEXPECTED_MISSING_ALGORITHM = 27; /** * The caller is attempting to perform an operation that is not yet fully supported in the API. */ public static final int ERROR_NOT_YET_SUPPORTED = 28; /** * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong, * the data has become corrupted, the data has been tampered with, etc. */ public static final int ERROR_DECRYPTION_FAILED = 29; /** * Rate limit is enforced to prevent using too many trusted remote devices, since each device * can have its own number of user secret guesses allowed. * * @hide */ public static final int RATE_LIMIT_EXCEEDED = 21; public static final int ERROR_RATE_LIMIT_EXCEEDED = 30; /** Key has been successfully synced. */ public static final int RECOVERY_STATUS_SYNCED = 0; Loading Loading @@ -89,12 +132,13 @@ public class RecoverableKeyStoreLoader { /** * Creates new {@link #RecoverableKeyStoreLoaderException} instance from the error code. * * @param errorCode * @param errorCode An error code, as listed at the top of this file. * @param message The associated error message. * @hide */ public static RecoverableKeyStoreLoaderException fromErrorCode(int errorCode) { return new RecoverableKeyStoreLoaderException( errorCode, getMessageFromErrorCode(errorCode)); public static RecoverableKeyStoreLoaderException fromErrorCode( int errorCode, String message) { return new RecoverableKeyStoreLoaderException(errorCode, message); } /** Loading @@ -106,33 +150,18 @@ public class RecoverableKeyStoreLoader { */ static RecoverableKeyStoreLoaderException fromServiceSpecificException( ServiceSpecificException e) throws RecoverableKeyStoreLoaderException { throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode); throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode, e.getMessage()); } private RecoverableKeyStoreLoaderException(int errorCode, String message) { super(message); mErrorCode = errorCode; } /** Returns errorCode. */ public int getErrorCode() { return mErrorCode; } /** @hide */ private static String getMessageFromErrorCode(int errorCode) { switch (errorCode) { case NO_ERROR: return "OK"; case SYSTEM_ERROR: return "System error"; case ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY: return "Recovery service is not initialized"; case RATE_LIMIT_EXCEEDED: return "Rate limit exceeded"; default: return String.valueOf("Unknown error code " + errorCode); } } } /** Loading services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +31 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,19 @@ package com.android.server.locksettings.recoverablekeystore; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_BAD_X509_CERTIFICATE; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader.ERROR_DATABASE_ERROR; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_DECRYPTION_FAILED; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader.ERROR_INSECURE_USER; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_KEYSTORE_INTERNAL_ERROR; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_NOT_YET_SUPPORTED; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_UNEXPECTED_MISSING_ALGORITHM; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; Loading Loading @@ -63,12 +76,6 @@ import javax.crypto.AEADBadTagException; public class RecoverableKeyStoreManager { private static final String TAG = "RecoverableKeyStoreMgr"; // TODO: move error codes to RecoverableKeyStoreLoader. private static int ERROR_INSECURE_USER = 1; private static int ERROR_KEYSTORE_INTERNAL_ERROR = 2; private static int ERROR_DATABASE_ERROR = 3; private static int ERROR_RECOVERY_SESSION_NOT_FOUND = 4; private static RecoverableKeyStoreManager mInstance; private final Context mContext; Loading Loading @@ -115,8 +122,8 @@ public class RecoverableKeyStoreManager { try { mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase); } catch (NoSuchAlgorithmException e) { // Impossible: all AOSP implementations must support AES. throw new RuntimeException(e); Log.wtf(TAG, "AES keygen algorithm not available. AOSP must support this.", e); throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); } } Loading @@ -133,10 +140,11 @@ public class RecoverableKeyStoreManager { X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(signedPublicKeyList); publicKey = kf.generatePublic(pkSpec); } catch (NoSuchAlgorithmException e) { // Should never happen throw new RuntimeException(e); Log.wtf(TAG, "EC algorithm not available. AOSP must support this.", e); throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); } catch (InvalidKeySpecException e) { throw new RemoteException("Invalid public key for the recovery service"); throw new ServiceSpecificException( ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 certificate."); } mDatabase.setRecoveryServicePublicKey(userId, Binder.getCallingUid(), publicKey); } Loading Loading @@ -295,7 +303,9 @@ public class RecoverableKeyStoreManager { if (secrets.size() != 1) { // TODO: support multiple secrets throw new RemoteException("Only a single KeyStoreRecoveryMetadata is supported"); throw new ServiceSpecificException( ERROR_NOT_YET_SUPPORTED, "Only a single KeyStoreRecoveryMetadata is supported"); } byte[] keyClaimant = KeySyncUtils.generateKeyClaimant(); Loading @@ -314,18 +324,11 @@ public class RecoverableKeyStoreManager { thmKfHash, keyClaimant); } catch (NoSuchAlgorithmException e) { // Should never happen: all the algorithms used are required by AOSP implementations. throw new RemoteException( "Missing required algorithm", e, /*enableSuppression=*/ true, /*writeableStackTrace=*/ true); Log.wtf(TAG, "SecureBox algorithm missing. AOSP must support this.", e); throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); } catch (InvalidKeySpecException | InvalidKeyException e) { throw new RemoteException( "Not a valid X509 key", e, /*enableSuppression=*/ true, /*writeableStackTrace=*/ true); throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 key"); } } Loading Loading @@ -439,18 +442,12 @@ public class RecoverableKeyStoreManager { KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial); keyMaterialByAlias.put(alias, keyMaterial); } catch (NoSuchAlgorithmException e) { // Should never happen: all the algorithms used are required by AOSP implementations throw new RemoteException( "Missing required algorithm", e, /*enableSuppression=*/ true, /*writeableStackTrace=*/ true); Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e); throw new ServiceSpecificException( ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); } catch (InvalidKeyException | AEADBadTagException e) { throw new RemoteException( "Failed to recover key with alias '" + alias + "'", e, /*enableSuppression=*/ true, /*writeableStackTrace=*/ true); throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED, "Failed to recover key with alias '" + alias + "': " + e.getMessage()); } } return keyMaterialByAlias; Loading Loading
core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java +51 −22 Original line number Diff line number Diff line Loading @@ -43,18 +43,61 @@ public class RecoverableKeyStoreLoader { public static final int NO_ERROR = KeyStore.NO_ERROR; public static final int SYSTEM_ERROR = KeyStore.SYSTEM_ERROR; /** * Failed because the loader has not been initialized with a recovery public key yet. */ public static final int ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20; /** * Failed because no snapshot is yet pending to be synced for the user. */ public static final int ERROR_NO_SNAPSHOT_PENDING = 21; /** * Failed due to an error internal to AndroidKeyStore. */ public static final int ERROR_KEYSTORE_INTERNAL_ERROR = 22; /** * Failed because the user does not have a lock screen set. */ public static final int ERROR_INSECURE_USER = 24; /** * Failed because of an internal database error. */ public static final int ERROR_DATABASE_ERROR = 25; /** * Failed because the provided certificate was not a valid X509 certificate. */ public static final int ERROR_BAD_X509_CERTIFICATE = 26; /** * Should never be thrown - some algorithm that all AOSP implementations must support is * not available. */ public static final int ERROR_UNEXPECTED_MISSING_ALGORITHM = 27; /** * The caller is attempting to perform an operation that is not yet fully supported in the API. */ public static final int ERROR_NOT_YET_SUPPORTED = 28; /** * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong, * the data has become corrupted, the data has been tampered with, etc. */ public static final int ERROR_DECRYPTION_FAILED = 29; /** * Rate limit is enforced to prevent using too many trusted remote devices, since each device * can have its own number of user secret guesses allowed. * * @hide */ public static final int RATE_LIMIT_EXCEEDED = 21; public static final int ERROR_RATE_LIMIT_EXCEEDED = 30; /** Key has been successfully synced. */ public static final int RECOVERY_STATUS_SYNCED = 0; Loading Loading @@ -89,12 +132,13 @@ public class RecoverableKeyStoreLoader { /** * Creates new {@link #RecoverableKeyStoreLoaderException} instance from the error code. * * @param errorCode * @param errorCode An error code, as listed at the top of this file. * @param message The associated error message. * @hide */ public static RecoverableKeyStoreLoaderException fromErrorCode(int errorCode) { return new RecoverableKeyStoreLoaderException( errorCode, getMessageFromErrorCode(errorCode)); public static RecoverableKeyStoreLoaderException fromErrorCode( int errorCode, String message) { return new RecoverableKeyStoreLoaderException(errorCode, message); } /** Loading @@ -106,33 +150,18 @@ public class RecoverableKeyStoreLoader { */ static RecoverableKeyStoreLoaderException fromServiceSpecificException( ServiceSpecificException e) throws RecoverableKeyStoreLoaderException { throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode); throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode, e.getMessage()); } private RecoverableKeyStoreLoaderException(int errorCode, String message) { super(message); mErrorCode = errorCode; } /** Returns errorCode. */ public int getErrorCode() { return mErrorCode; } /** @hide */ private static String getMessageFromErrorCode(int errorCode) { switch (errorCode) { case NO_ERROR: return "OK"; case SYSTEM_ERROR: return "System error"; case ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY: return "Recovery service is not initialized"; case RATE_LIMIT_EXCEEDED: return "Rate limit exceeded"; default: return String.valueOf("Unknown error code " + errorCode); } } } /** Loading
services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +31 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,19 @@ package com.android.server.locksettings.recoverablekeystore; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_BAD_X509_CERTIFICATE; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader.ERROR_DATABASE_ERROR; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_DECRYPTION_FAILED; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader.ERROR_INSECURE_USER; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_KEYSTORE_INTERNAL_ERROR; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_NOT_YET_SUPPORTED; import static android.security.recoverablekeystore.RecoverableKeyStoreLoader .ERROR_UNEXPECTED_MISSING_ALGORITHM; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; Loading Loading @@ -63,12 +76,6 @@ import javax.crypto.AEADBadTagException; public class RecoverableKeyStoreManager { private static final String TAG = "RecoverableKeyStoreMgr"; // TODO: move error codes to RecoverableKeyStoreLoader. private static int ERROR_INSECURE_USER = 1; private static int ERROR_KEYSTORE_INTERNAL_ERROR = 2; private static int ERROR_DATABASE_ERROR = 3; private static int ERROR_RECOVERY_SESSION_NOT_FOUND = 4; private static RecoverableKeyStoreManager mInstance; private final Context mContext; Loading Loading @@ -115,8 +122,8 @@ public class RecoverableKeyStoreManager { try { mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase); } catch (NoSuchAlgorithmException e) { // Impossible: all AOSP implementations must support AES. throw new RuntimeException(e); Log.wtf(TAG, "AES keygen algorithm not available. AOSP must support this.", e); throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); } } Loading @@ -133,10 +140,11 @@ public class RecoverableKeyStoreManager { X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(signedPublicKeyList); publicKey = kf.generatePublic(pkSpec); } catch (NoSuchAlgorithmException e) { // Should never happen throw new RuntimeException(e); Log.wtf(TAG, "EC algorithm not available. AOSP must support this.", e); throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); } catch (InvalidKeySpecException e) { throw new RemoteException("Invalid public key for the recovery service"); throw new ServiceSpecificException( ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 certificate."); } mDatabase.setRecoveryServicePublicKey(userId, Binder.getCallingUid(), publicKey); } Loading Loading @@ -295,7 +303,9 @@ public class RecoverableKeyStoreManager { if (secrets.size() != 1) { // TODO: support multiple secrets throw new RemoteException("Only a single KeyStoreRecoveryMetadata is supported"); throw new ServiceSpecificException( ERROR_NOT_YET_SUPPORTED, "Only a single KeyStoreRecoveryMetadata is supported"); } byte[] keyClaimant = KeySyncUtils.generateKeyClaimant(); Loading @@ -314,18 +324,11 @@ public class RecoverableKeyStoreManager { thmKfHash, keyClaimant); } catch (NoSuchAlgorithmException e) { // Should never happen: all the algorithms used are required by AOSP implementations. throw new RemoteException( "Missing required algorithm", e, /*enableSuppression=*/ true, /*writeableStackTrace=*/ true); Log.wtf(TAG, "SecureBox algorithm missing. AOSP must support this.", e); throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); } catch (InvalidKeySpecException | InvalidKeyException e) { throw new RemoteException( "Not a valid X509 key", e, /*enableSuppression=*/ true, /*writeableStackTrace=*/ true); throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 key"); } } Loading Loading @@ -439,18 +442,12 @@ public class RecoverableKeyStoreManager { KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial); keyMaterialByAlias.put(alias, keyMaterial); } catch (NoSuchAlgorithmException e) { // Should never happen: all the algorithms used are required by AOSP implementations throw new RemoteException( "Missing required algorithm", e, /*enableSuppression=*/ true, /*writeableStackTrace=*/ true); Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e); throw new ServiceSpecificException( ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage()); } catch (InvalidKeyException | AEADBadTagException e) { throw new RemoteException( "Failed to recover key with alias '" + alias + "'", e, /*enableSuppression=*/ true, /*writeableStackTrace=*/ true); throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED, "Failed to recover key with alias '" + alias + "': " + e.getMessage()); } } return keyMaterialByAlias; Loading