diff --git a/api/current.txt b/api/current.txt index 61465acd362cb..8d29782e1a90e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11197,6 +11197,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_SENSOR_STEP_DETECTOR = "android.hardware.sensor.stepdetector"; field public static final java.lang.String FEATURE_SIP = "android.software.sip"; field public static final java.lang.String FEATURE_SIP_VOIP = "android.software.sip.voip"; + field public static final java.lang.String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore"; field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony"; field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma"; field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm"; @@ -38233,7 +38234,6 @@ package android.security.keystore { } public class StrongBoxUnavailableException extends java.security.ProviderException { - ctor public StrongBoxUnavailableException(); } public class UserNotAuthenticatedException extends java.security.InvalidKeyException { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 67c9584b01f75..bdb0eb621c5fc 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2593,6 +2593,14 @@ public abstract class PackageManager { @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking"; + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device has a StrongBox hardware-backed Keystore. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_STRONGBOX_KEYSTORE = + "android.hardware.strongbox_keystore"; + /** * Action to external storage service to clean out removed apps. * @hide diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index e25386baf969f..8547f486d3700 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -38,6 +38,7 @@ import android.security.keymaster.OperationResult; import android.security.keystore.KeyExpiredException; import android.security.keystore.KeyNotYetValidException; import android.security.keystore.KeyPermanentlyInvalidatedException; +import android.security.keystore.StrongBoxUnavailableException; import android.security.keystore.UserNotAuthenticatedException; import android.util.Log; @@ -65,6 +66,7 @@ public class KeyStore { public static final int VALUE_CORRUPTED = 8; public static final int UNDEFINED_ACTION = 9; public static final int WRONG_PASSWORD = 10; + public static final int HARDWARE_TYPE_UNAVAILABLE = -68; /** * Per operation authentication is needed before this operation is valid. @@ -123,7 +125,6 @@ public class KeyStore { */ public static final int FLAG_STRONGBOX = 1 << 4; - // States public enum State { UNLOCKED, LOCKED, UNINITIALIZED }; diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index dba3949ba98f9..d1eb6888bbfdc 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -21,6 +21,7 @@ import android.security.Credentials; import android.security.GateKeeper; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore; +import android.security.KeyStoreException; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterCertificateChain; @@ -451,7 +452,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato throw new IllegalStateException("Not initialized"); } - final int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0; + int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0; if (((flags & KeyStore.FLAG_ENCRYPTED) != 0) && (mKeyStore.state() != KeyStore.State.UNLOCKED)) { throw new IllegalStateException( @@ -459,6 +460,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato + ", but the user has not yet entered the credential"); } + if (mSpec.isStrongBoxBacked()) { + flags |= KeyStore.FLAG_STRONGBOX; + } + byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( mRng, (mKeySizeBits + 7) / 8); @@ -501,8 +506,12 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato int errorCode = mKeyStore.generateKey(privateKeyAlias, args, additionalEntropy, mEntryUid, flags, resultingKeyCharacteristics); if (errorCode != KeyStore.NO_ERROR) { - throw new ProviderException( - "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode)); + if (errorCode == KeyStore.HARDWARE_TYPE_UNAVAILABLE) { + throw new StrongBoxUnavailableException("Failed to generate key pair"); + } else { + throw new ProviderException( + "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode)); + } } } diff --git a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java index ad41a58eb76f5..66a77ed1a0de8 100644 --- a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java +++ b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java @@ -16,6 +16,9 @@ package android.security.keystore; +import android.security.KeyStore; +import android.security.KeyStoreException; + import java.security.ProviderException; /** @@ -24,5 +27,13 @@ import java.security.ProviderException; */ public class StrongBoxUnavailableException extends ProviderException { + /** + * @hide + */ + public StrongBoxUnavailableException(String message) { + super(message, + new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE, "No StrongBox available") + ); + } }