Loading core/api/current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -37578,10 +37578,12 @@ package android.security { public class KeyStoreException extends java.lang.Exception { method public int getNumericErrorCode(); method public int getRetryPolicy(); method public boolean isSystemError(); method public boolean isTransientFailure(); method public boolean requiresUserAuthentication(); field public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9; // 0x9 field public static final int ERROR_ATTESTATION_KEYS_UNAVAILABLE = 16; // 0x10 field public static final int ERROR_ID_ATTESTATION_FAILURE = 8; // 0x8 field public static final int ERROR_INCORRECT_USAGE = 13; // 0xd field public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4; // 0x4 Loading @@ -37596,6 +37598,9 @@ package android.security { field public static final int ERROR_PERMISSION_DENIED = 5; // 0x5 field public static final int ERROR_UNIMPLEMENTED = 12; // 0xc field public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2; // 0x2 field public static final int RETRY_NEVER = 1; // 0x1 field public static final int RETRY_WHEN_CONNECTIVITY_AVAILABLE = 3; // 0x3 field public static final int RETRY_WITH_EXPONENTIAL_BACKOFF = 2; // 0x2 } @Deprecated public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter { keystore/java/android/security/KeyStoreException.java +152 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.security.keymaster.KeymasterDefs; import android.system.keystore2.ResponseCode; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading @@ -36,6 +37,8 @@ import java.util.Map; * is likely to succeed. */ public class KeyStoreException extends Exception { private static final String TAG = "KeyStoreException"; /** * This error code is for mapping errors that the caller will not know about. If the caller is * targeting an API level earlier than the one the error was introduced in, then the error will Loading Loading @@ -114,6 +117,27 @@ public class KeyStoreException extends Exception { * The caller should re-create the crypto object and try again. */ public static final int ERROR_KEY_OPERATION_EXPIRED = 15; /** * There are no keys available for attestation. * This error is returned only on devices that rely solely on remotely-provisioned keys (see * <a href= * "https://android-developers.googleblog.com/2022/03/upgrading-android-attestation-remote.html" * >Remote Key Provisioning</a>). * * <p>On such a device, if the caller requests key generation and includes an attestation * challenge (indicating key attestation is required), the error will be returned in one of * the following cases: * <ul> * <li>The pool of remotely-provisioned keys has been exhausted.</li> * <li>The device is not registered with the key provisioning server.</li> * </ul> * </p> * * <p>This error is a transient one if the pool of remotely-provisioned keys has been * exhausted. However, if the device is not registered with the server, or the key * provisioning server refuses key issuance, this is a permanent error.</p> */ public static final int ERROR_ATTESTATION_KEYS_UNAVAILABLE = 16; /** @hide */ @Retention(RetentionPolicy.SOURCE) Loading @@ -132,11 +156,68 @@ public class KeyStoreException extends Exception { ERROR_UNIMPLEMENTED, ERROR_INCORRECT_USAGE, ERROR_KEY_NOT_TEMPORALLY_VALID, ERROR_KEY_OPERATION_EXPIRED ERROR_KEY_OPERATION_EXPIRED, ERROR_ATTESTATION_KEYS_UNAVAILABLE }) public @interface PublicErrorCode { } /** * Never re-try the operation that led to this error, since it's a permanent error. * * This value is always returned when {@link #isTransientFailure()} is {@code false}. */ public static final int RETRY_NEVER = 1; /** * Re-try the operation that led to this error with an exponential back-off delay. * The first delay should be between 5 to 30 seconds, and each subsequent re-try should double * the delay time. * * This value is returned when {@link #isTransientFailure()} is {@code true}. */ public static final int RETRY_WITH_EXPONENTIAL_BACKOFF = 2; /** * Re-try the operation that led to this error when the device regains connectivity. * Remote provisioning of keys requires reaching the remote server, and the device is * currently unable to due that due to lack of network connectivity. * * This value is returned when {@link #isTransientFailure()} is {@code true}. */ public static final int RETRY_WHEN_CONNECTIVITY_AVAILABLE = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = {"RETRY_"}, value = { RETRY_NEVER, RETRY_WITH_EXPONENTIAL_BACKOFF, RETRY_WHEN_CONNECTIVITY_AVAILABLE, }) public @interface RetryPolicy { } // RKP-specific error information. /** * Remote provisioning of attestation keys has completed successfully. * @hide */ public static final int RKP_SUCCESS = 0; /** * Remotely-provisioned keys are temporarily unavailable. This could be because of RPC * error when talking to the remote provisioner or keys are being currently fetched and will * be available soon. * @hide */ public static final int RKP_TEMPORARILY_UNAVAILABLE = 1; /** * Permanent failure: The RKP server has declined issuance of keys to this device. Either * because the device is not registered with the server or the server considers the device * not to be trustworthy. * @hide */ public static final int RKP_SERVER_REFUSED_ISSUANCE = 2; /** * The RKP server is unavailable due to lack of connectivity. The caller should re-try * when the device has connectivity again. * @hide */ public static final int RKP_FETCHING_PENDING_CONNECTIVITY = 3; // Constants for encoding information about the error encountered: // Whether the error relates to the system state/implementation as a whole, or a specific key. private static final int IS_SYSTEM_ERROR = 1 << 1; Loading @@ -148,6 +229,21 @@ public class KeyStoreException extends Exception { // The internal error code. NOT to be returned directly to callers or made part of the // public API. private final int mErrorCode; // The Remote Key Provisioning status. Applicable if and only if {@link #mErrorCode} is equal // to {@link ResponseCode.OUT_OF_KEYS}. private final int mRkpStatus; private static int initializeRkpStatusForRegularErrors(int errorCode) { // Check if the system code mistakenly called a constructor of KeyStoreException with // the OUT_OF_KEYS error code but without RKP status. if (errorCode == ResponseCode.OUT_OF_KEYS) { Log.e(TAG, "RKP error code without RKP status"); // Set RKP status to RKP_SERVER_REFUSED_ISSUANCE so that the caller never retries. return RKP_SERVER_REFUSED_ISSUANCE; } else { return RKP_SUCCESS; } } /** * @hide Loading @@ -155,6 +251,7 @@ public class KeyStoreException extends Exception { public KeyStoreException(int errorCode, @Nullable String message) { super(message); mErrorCode = errorCode; mRkpStatus = initializeRkpStatusForRegularErrors(errorCode); } /** Loading @@ -165,6 +262,19 @@ public class KeyStoreException extends Exception { super(message + " (internal Keystore code: " + errorCode + " message: " + keystoreErrorMessage + ")"); mErrorCode = errorCode; mRkpStatus = initializeRkpStatusForRegularErrors(errorCode); } /** * @hide */ public KeyStoreException(int errorCode, @Nullable String message, int rkpStatus) { super(message); mErrorCode = errorCode; mRkpStatus = rkpStatus; if (mErrorCode != ResponseCode.OUT_OF_KEYS) { Log.e(TAG, "Providing RKP status for error code " + errorCode + " has no effect."); } } /** Loading Loading @@ -198,6 +308,17 @@ public class KeyStoreException extends Exception { */ public boolean isTransientFailure() { PublicErrorInformation failureInfo = getErrorInformation(mErrorCode); // Special-case handling for RKP failures: if (mRkpStatus != RKP_SUCCESS && mErrorCode == ResponseCode.OUT_OF_KEYS) { switch (mRkpStatus) { case RKP_TEMPORARILY_UNAVAILABLE: case RKP_FETCHING_PENDING_CONNECTIVITY: return true; case RKP_SERVER_REFUSED_ISSUANCE: default: return false; } } return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0; } Loading Loading @@ -225,6 +346,34 @@ public class KeyStoreException extends Exception { return (failureInfo.indicators & IS_SYSTEM_ERROR) != 0; } /** * Returns the re-try policy for transient failures. Valid only if * {@link #isTransientFailure()} returns {@code True}. */ @RetryPolicy public int getRetryPolicy() { PublicErrorInformation failureInfo = getErrorInformation(mErrorCode); // Special-case handling for RKP failures: if (mRkpStatus != RKP_SUCCESS) { switch (mRkpStatus) { case RKP_TEMPORARILY_UNAVAILABLE: return RETRY_WITH_EXPONENTIAL_BACKOFF; case RKP_FETCHING_PENDING_CONNECTIVITY: return RETRY_WHEN_CONNECTIVITY_AVAILABLE; case RKP_SERVER_REFUSED_ISSUANCE: return RETRY_NEVER; default: return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0 ? RETRY_WITH_EXPONENTIAL_BACKOFF : RETRY_NEVER; } } if ((failureInfo.indicators & IS_TRANSIENT_ERROR) != 0) { return RETRY_WITH_EXPONENTIAL_BACKOFF; } else { return RETRY_NEVER; } } @Override public String toString() { String errorCodes = String.format(" (public error code: %d internal Keystore code: %d)", Loading Loading @@ -469,5 +618,7 @@ public class KeyStoreException extends Exception { new PublicErrorInformation(0, ERROR_KEY_CORRUPTED)); sErrorCodeToFailureInfo.put(ResponseCode.KEY_PERMANENTLY_INVALIDATED, new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST)); sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS, new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_ATTESTATION_KEYS_UNAVAILABLE)); } } keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +46 −12 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import android.hardware.security.keymint.Tag; import android.os.Build; import android.os.RemoteException; import android.security.GenerateRkpKey; import android.security.GenerateRkpKeyException; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore2; import android.security.KeyStoreException; Loading Loading @@ -618,18 +617,44 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato @Override public KeyPair generateKeyPair() { try { return generateKeyPairHelper(); } catch (GenerateRkpKeyException e) { try { return generateKeyPairHelper(); } catch (GenerateRkpKeyException f) { throw new ProviderException("Failed to provision new attestation keys."); GenerateKeyPairHelperResult result = new GenerateKeyPairHelperResult(0, null); for (int i = 0; i < 2; i++) { /** * NOTE: There is no need to delay between re-tries because the call to * GenerateRkpKey.notifyEmpty() will delay for a while before returning. */ result = generateKeyPairHelper(); if (result.rkpStatus == KeyStoreException.RKP_SUCCESS) { return result.keyPair; } } // RKP failure if (result.rkpStatus != KeyStoreException.RKP_SUCCESS) { KeyStoreException ksException = new KeyStoreException(ResponseCode.OUT_OF_KEYS, "Could not get RKP keys", result.rkpStatus); throw new ProviderException("Failed to provision new attestation keys.", ksException); } return result.keyPair; } private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException { private static class GenerateKeyPairHelperResult { // Zero indicates success, non-zero indicates failure. Values should be // {@link android.security.KeyStoreException#RKP_TEMPORARILY_UNAVAILABLE}, // {@link android.security.KeyStoreException#RKP_SERVER_REFUSED_ISSUANCE}, // {@link android.security.KeyStoreException#RKP_FETCHING_PENDING_CONNECTIVITY} public final int rkpStatus; @Nullable public final KeyPair keyPair; private GenerateKeyPairHelperResult(int rkpStatus, KeyPair keyPair) { this.rkpStatus = rkpStatus; this.keyPair = keyPair; } } private GenerateKeyPairHelperResult generateKeyPairHelper() { if (mKeyStore == null || mSpec == null) { throw new IllegalStateException("Not initialized"); } Loading Loading @@ -679,7 +704,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.", e); } success = true; return new KeyPair(publicKey, publicKey.getPrivateKey()); KeyPair kp = new KeyPair(publicKey, publicKey.getPrivateKey()); return new GenerateKeyPairHelperResult(0, kp); } catch (android.security.KeyStoreException e) { switch (e.getErrorCode()) { case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE: Loading @@ -688,11 +714,19 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread .currentApplication()); try { //TODO: When detailed error information is available from the remote //provisioner, propagate it up. keyGen.notifyEmpty(securityLevel); } catch (RemoteException f) { throw new ProviderException("Failed to talk to RemoteProvisioner", f); } throw new GenerateRkpKeyException(); KeyStoreException ksException = new KeyStoreException( ResponseCode.OUT_OF_KEYS, "Remote exception: " + f.getMessage(), KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE); throw new ProviderException("Failed to talk to RemoteProvisioner", ksException); } return new GenerateKeyPairHelperResult( KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE, null); default: ProviderException p = new ProviderException("Failed to generate key pair.", e); if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { Loading Loading
core/api/current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -37578,10 +37578,12 @@ package android.security { public class KeyStoreException extends java.lang.Exception { method public int getNumericErrorCode(); method public int getRetryPolicy(); method public boolean isSystemError(); method public boolean isTransientFailure(); method public boolean requiresUserAuthentication(); field public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9; // 0x9 field public static final int ERROR_ATTESTATION_KEYS_UNAVAILABLE = 16; // 0x10 field public static final int ERROR_ID_ATTESTATION_FAILURE = 8; // 0x8 field public static final int ERROR_INCORRECT_USAGE = 13; // 0xd field public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4; // 0x4 Loading @@ -37596,6 +37598,9 @@ package android.security { field public static final int ERROR_PERMISSION_DENIED = 5; // 0x5 field public static final int ERROR_UNIMPLEMENTED = 12; // 0xc field public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2; // 0x2 field public static final int RETRY_NEVER = 1; // 0x1 field public static final int RETRY_WHEN_CONNECTIVITY_AVAILABLE = 3; // 0x3 field public static final int RETRY_WITH_EXPONENTIAL_BACKOFF = 2; // 0x2 } @Deprecated public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
keystore/java/android/security/KeyStoreException.java +152 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.security.keymaster.KeymasterDefs; import android.system.keystore2.ResponseCode; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading @@ -36,6 +37,8 @@ import java.util.Map; * is likely to succeed. */ public class KeyStoreException extends Exception { private static final String TAG = "KeyStoreException"; /** * This error code is for mapping errors that the caller will not know about. If the caller is * targeting an API level earlier than the one the error was introduced in, then the error will Loading Loading @@ -114,6 +117,27 @@ public class KeyStoreException extends Exception { * The caller should re-create the crypto object and try again. */ public static final int ERROR_KEY_OPERATION_EXPIRED = 15; /** * There are no keys available for attestation. * This error is returned only on devices that rely solely on remotely-provisioned keys (see * <a href= * "https://android-developers.googleblog.com/2022/03/upgrading-android-attestation-remote.html" * >Remote Key Provisioning</a>). * * <p>On such a device, if the caller requests key generation and includes an attestation * challenge (indicating key attestation is required), the error will be returned in one of * the following cases: * <ul> * <li>The pool of remotely-provisioned keys has been exhausted.</li> * <li>The device is not registered with the key provisioning server.</li> * </ul> * </p> * * <p>This error is a transient one if the pool of remotely-provisioned keys has been * exhausted. However, if the device is not registered with the server, or the key * provisioning server refuses key issuance, this is a permanent error.</p> */ public static final int ERROR_ATTESTATION_KEYS_UNAVAILABLE = 16; /** @hide */ @Retention(RetentionPolicy.SOURCE) Loading @@ -132,11 +156,68 @@ public class KeyStoreException extends Exception { ERROR_UNIMPLEMENTED, ERROR_INCORRECT_USAGE, ERROR_KEY_NOT_TEMPORALLY_VALID, ERROR_KEY_OPERATION_EXPIRED ERROR_KEY_OPERATION_EXPIRED, ERROR_ATTESTATION_KEYS_UNAVAILABLE }) public @interface PublicErrorCode { } /** * Never re-try the operation that led to this error, since it's a permanent error. * * This value is always returned when {@link #isTransientFailure()} is {@code false}. */ public static final int RETRY_NEVER = 1; /** * Re-try the operation that led to this error with an exponential back-off delay. * The first delay should be between 5 to 30 seconds, and each subsequent re-try should double * the delay time. * * This value is returned when {@link #isTransientFailure()} is {@code true}. */ public static final int RETRY_WITH_EXPONENTIAL_BACKOFF = 2; /** * Re-try the operation that led to this error when the device regains connectivity. * Remote provisioning of keys requires reaching the remote server, and the device is * currently unable to due that due to lack of network connectivity. * * This value is returned when {@link #isTransientFailure()} is {@code true}. */ public static final int RETRY_WHEN_CONNECTIVITY_AVAILABLE = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = {"RETRY_"}, value = { RETRY_NEVER, RETRY_WITH_EXPONENTIAL_BACKOFF, RETRY_WHEN_CONNECTIVITY_AVAILABLE, }) public @interface RetryPolicy { } // RKP-specific error information. /** * Remote provisioning of attestation keys has completed successfully. * @hide */ public static final int RKP_SUCCESS = 0; /** * Remotely-provisioned keys are temporarily unavailable. This could be because of RPC * error when talking to the remote provisioner or keys are being currently fetched and will * be available soon. * @hide */ public static final int RKP_TEMPORARILY_UNAVAILABLE = 1; /** * Permanent failure: The RKP server has declined issuance of keys to this device. Either * because the device is not registered with the server or the server considers the device * not to be trustworthy. * @hide */ public static final int RKP_SERVER_REFUSED_ISSUANCE = 2; /** * The RKP server is unavailable due to lack of connectivity. The caller should re-try * when the device has connectivity again. * @hide */ public static final int RKP_FETCHING_PENDING_CONNECTIVITY = 3; // Constants for encoding information about the error encountered: // Whether the error relates to the system state/implementation as a whole, or a specific key. private static final int IS_SYSTEM_ERROR = 1 << 1; Loading @@ -148,6 +229,21 @@ public class KeyStoreException extends Exception { // The internal error code. NOT to be returned directly to callers or made part of the // public API. private final int mErrorCode; // The Remote Key Provisioning status. Applicable if and only if {@link #mErrorCode} is equal // to {@link ResponseCode.OUT_OF_KEYS}. private final int mRkpStatus; private static int initializeRkpStatusForRegularErrors(int errorCode) { // Check if the system code mistakenly called a constructor of KeyStoreException with // the OUT_OF_KEYS error code but without RKP status. if (errorCode == ResponseCode.OUT_OF_KEYS) { Log.e(TAG, "RKP error code without RKP status"); // Set RKP status to RKP_SERVER_REFUSED_ISSUANCE so that the caller never retries. return RKP_SERVER_REFUSED_ISSUANCE; } else { return RKP_SUCCESS; } } /** * @hide Loading @@ -155,6 +251,7 @@ public class KeyStoreException extends Exception { public KeyStoreException(int errorCode, @Nullable String message) { super(message); mErrorCode = errorCode; mRkpStatus = initializeRkpStatusForRegularErrors(errorCode); } /** Loading @@ -165,6 +262,19 @@ public class KeyStoreException extends Exception { super(message + " (internal Keystore code: " + errorCode + " message: " + keystoreErrorMessage + ")"); mErrorCode = errorCode; mRkpStatus = initializeRkpStatusForRegularErrors(errorCode); } /** * @hide */ public KeyStoreException(int errorCode, @Nullable String message, int rkpStatus) { super(message); mErrorCode = errorCode; mRkpStatus = rkpStatus; if (mErrorCode != ResponseCode.OUT_OF_KEYS) { Log.e(TAG, "Providing RKP status for error code " + errorCode + " has no effect."); } } /** Loading Loading @@ -198,6 +308,17 @@ public class KeyStoreException extends Exception { */ public boolean isTransientFailure() { PublicErrorInformation failureInfo = getErrorInformation(mErrorCode); // Special-case handling for RKP failures: if (mRkpStatus != RKP_SUCCESS && mErrorCode == ResponseCode.OUT_OF_KEYS) { switch (mRkpStatus) { case RKP_TEMPORARILY_UNAVAILABLE: case RKP_FETCHING_PENDING_CONNECTIVITY: return true; case RKP_SERVER_REFUSED_ISSUANCE: default: return false; } } return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0; } Loading Loading @@ -225,6 +346,34 @@ public class KeyStoreException extends Exception { return (failureInfo.indicators & IS_SYSTEM_ERROR) != 0; } /** * Returns the re-try policy for transient failures. Valid only if * {@link #isTransientFailure()} returns {@code True}. */ @RetryPolicy public int getRetryPolicy() { PublicErrorInformation failureInfo = getErrorInformation(mErrorCode); // Special-case handling for RKP failures: if (mRkpStatus != RKP_SUCCESS) { switch (mRkpStatus) { case RKP_TEMPORARILY_UNAVAILABLE: return RETRY_WITH_EXPONENTIAL_BACKOFF; case RKP_FETCHING_PENDING_CONNECTIVITY: return RETRY_WHEN_CONNECTIVITY_AVAILABLE; case RKP_SERVER_REFUSED_ISSUANCE: return RETRY_NEVER; default: return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0 ? RETRY_WITH_EXPONENTIAL_BACKOFF : RETRY_NEVER; } } if ((failureInfo.indicators & IS_TRANSIENT_ERROR) != 0) { return RETRY_WITH_EXPONENTIAL_BACKOFF; } else { return RETRY_NEVER; } } @Override public String toString() { String errorCodes = String.format(" (public error code: %d internal Keystore code: %d)", Loading Loading @@ -469,5 +618,7 @@ public class KeyStoreException extends Exception { new PublicErrorInformation(0, ERROR_KEY_CORRUPTED)); sErrorCodeToFailureInfo.put(ResponseCode.KEY_PERMANENTLY_INVALIDATED, new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST)); sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS, new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_ATTESTATION_KEYS_UNAVAILABLE)); } }
keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +46 −12 Original line number Diff line number Diff line Loading @@ -28,7 +28,6 @@ import android.hardware.security.keymint.Tag; import android.os.Build; import android.os.RemoteException; import android.security.GenerateRkpKey; import android.security.GenerateRkpKeyException; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore2; import android.security.KeyStoreException; Loading Loading @@ -618,18 +617,44 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato @Override public KeyPair generateKeyPair() { try { return generateKeyPairHelper(); } catch (GenerateRkpKeyException e) { try { return generateKeyPairHelper(); } catch (GenerateRkpKeyException f) { throw new ProviderException("Failed to provision new attestation keys."); GenerateKeyPairHelperResult result = new GenerateKeyPairHelperResult(0, null); for (int i = 0; i < 2; i++) { /** * NOTE: There is no need to delay between re-tries because the call to * GenerateRkpKey.notifyEmpty() will delay for a while before returning. */ result = generateKeyPairHelper(); if (result.rkpStatus == KeyStoreException.RKP_SUCCESS) { return result.keyPair; } } // RKP failure if (result.rkpStatus != KeyStoreException.RKP_SUCCESS) { KeyStoreException ksException = new KeyStoreException(ResponseCode.OUT_OF_KEYS, "Could not get RKP keys", result.rkpStatus); throw new ProviderException("Failed to provision new attestation keys.", ksException); } return result.keyPair; } private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException { private static class GenerateKeyPairHelperResult { // Zero indicates success, non-zero indicates failure. Values should be // {@link android.security.KeyStoreException#RKP_TEMPORARILY_UNAVAILABLE}, // {@link android.security.KeyStoreException#RKP_SERVER_REFUSED_ISSUANCE}, // {@link android.security.KeyStoreException#RKP_FETCHING_PENDING_CONNECTIVITY} public final int rkpStatus; @Nullable public final KeyPair keyPair; private GenerateKeyPairHelperResult(int rkpStatus, KeyPair keyPair) { this.rkpStatus = rkpStatus; this.keyPair = keyPair; } } private GenerateKeyPairHelperResult generateKeyPairHelper() { if (mKeyStore == null || mSpec == null) { throw new IllegalStateException("Not initialized"); } Loading Loading @@ -679,7 +704,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.", e); } success = true; return new KeyPair(publicKey, publicKey.getPrivateKey()); KeyPair kp = new KeyPair(publicKey, publicKey.getPrivateKey()); return new GenerateKeyPairHelperResult(0, kp); } catch (android.security.KeyStoreException e) { switch (e.getErrorCode()) { case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE: Loading @@ -688,11 +714,19 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread .currentApplication()); try { //TODO: When detailed error information is available from the remote //provisioner, propagate it up. keyGen.notifyEmpty(securityLevel); } catch (RemoteException f) { throw new ProviderException("Failed to talk to RemoteProvisioner", f); } throw new GenerateRkpKeyException(); KeyStoreException ksException = new KeyStoreException( ResponseCode.OUT_OF_KEYS, "Remote exception: " + f.getMessage(), KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE); throw new ProviderException("Failed to talk to RemoteProvisioner", ksException); } return new GenerateKeyPairHelperResult( KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE, null); default: ProviderException p = new ProviderException("Failed to generate key pair.", e); if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { Loading