diff --git a/api/current.txt b/api/current.txt index b6aaa204213a4..61ecf26434bc3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -42745,6 +42745,7 @@ package android.security.keystore { method @NonNull public String[] getSignaturePaddings(); method public int getUserAuthenticationType(); method public int getUserAuthenticationValidityDurationSeconds(); + method public boolean isDevicePropertiesAttestationIncluded(); method @NonNull public boolean isDigestsSpecified(); method public boolean isInvalidatedByBiometricEnrollment(); method public boolean isRandomizedEncryptionRequired(); @@ -42766,6 +42767,7 @@ package android.security.keystore { method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateNotBefore(@NonNull java.util.Date); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSerialNumber(@NonNull java.math.BigInteger); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSubject(@NonNull javax.security.auth.x500.X500Principal); + method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setDevicePropertiesAttestationIncluded(boolean); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...); method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index c52fd48459cb4..d4cf53e874f17 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -17,6 +17,7 @@ package android.security.keystore; import android.annotation.Nullable; +import android.os.Build; import android.security.Credentials; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore; @@ -50,6 +51,7 @@ import libcore.util.EmptyArray; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -495,6 +497,20 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato if (challenge != null) { KeymasterArguments args = new KeymasterArguments(); args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge); + + if (mSpec.isDevicePropertiesAttestationIncluded()) { + args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND, + Build.BRAND.getBytes(StandardCharsets.UTF_8)); + args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE, + Build.DEVICE.getBytes(StandardCharsets.UTF_8)); + args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT, + Build.PRODUCT.getBytes(StandardCharsets.UTF_8)); + args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER, + Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8)); + args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL, + Build.MODEL.getBytes(StandardCharsets.UTF_8)); + } + return getAttestationChain(privateKeyAlias, keyPair, args); } @@ -604,8 +620,14 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private Iterable getAttestationChain(String privateKeyAlias, KeyPair keyPair, KeymasterArguments args) throws ProviderException { - KeymasterCertificateChain outChain = new KeymasterCertificateChain(); - int errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain); + final KeymasterCertificateChain outChain = new KeymasterCertificateChain(); + final int errorCode; + if (mSpec.isDevicePropertiesAttestationIncluded() + && mSpec.getAttestationChallenge() == null) { + throw new ProviderException("An attestation challenge must be provided when requesting " + + "device properties attestation."); + } + errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain); if (errorCode != KeyStore.NO_ERROR) { throw new ProviderException("Failed to generate attestation certificate chain", KeyStore.getKeyStoreException(errorCode)); diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index a7d0cb84f848b..688c4a7b5969c 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -25,6 +25,7 @@ import android.app.KeyguardManager; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; +import android.os.Build; import android.security.GateKeeper; import android.security.KeyStore; import android.text.TextUtils; @@ -266,6 +267,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final @KeyProperties.AuthEnum int mUserAuthenticationType; private final boolean mUserPresenceRequired; private final byte[] mAttestationChallenge; + private final boolean mDevicePropertiesAttestationIncluded; private final boolean mUniqueIdIncluded; private final boolean mUserAuthenticationValidWhileOnBody; private final boolean mInvalidatedByBiometricEnrollment; @@ -305,6 +307,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu @KeyProperties.AuthEnum int userAuthenticationType, boolean userPresenceRequired, byte[] attestationChallenge, + boolean devicePropertiesAttestationIncluded, boolean uniqueIdIncluded, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, @@ -356,6 +359,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; mUserAuthenticationType = userAuthenticationType; mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge); + mDevicePropertiesAttestationIncluded = devicePropertiesAttestationIncluded; mUniqueIdIncluded = uniqueIdIncluded; mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; @@ -666,6 +670,21 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu return Utils.cloneIfNotNull(mAttestationChallenge); } + /** + * Returns {@code true} if attestation for the base device properties ({@link Build#BRAND}, + * {@link Build#DEVICE}, {@link Build#MANUFACTURER}, {@link Build#MODEL}, {@link Build#PRODUCT}) + * was requested to be added in the attestation certificate for the generated key. + * + * {@link javax.crypto.KeyGenerator#generateKey()} will throw + * {@link java.security.ProviderException} if device properties attestation fails or is not + * supported. + * + * @see Builder#setDevicePropertiesAttestationIncluded(boolean) + */ + public boolean isDevicePropertiesAttestationIncluded() { + return mDevicePropertiesAttestationIncluded; + } + /** * @hide This is a system-only API * @@ -769,6 +788,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu KeyProperties.AUTH_BIOMETRIC_STRONG; private boolean mUserPresenceRequired = false; private byte[] mAttestationChallenge = null; + private boolean mDevicePropertiesAttestationIncluded = false; private boolean mUniqueIdIncluded = false; private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; @@ -834,6 +854,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUserAuthenticationType = sourceSpec.getUserAuthenticationType(); mUserPresenceRequired = sourceSpec.isUserPresenceRequired(); mAttestationChallenge = sourceSpec.getAttestationChallenge(); + mDevicePropertiesAttestationIncluded = + sourceSpec.isDevicePropertiesAttestationIncluded(); mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded(); mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody(); mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment(); @@ -1339,6 +1361,31 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu return this; } + /** + * Sets whether to include the base device properties in the attestation certificate. + * + *

If {@code attestationChallenge} is not {@code null}, the public key certificate for + * this key pair will contain an extension that describes the details of the key's + * configuration and authorizations, including the device properties values (brand, device, + * manufacturer, model, product). These should be the same as in ({@link Build#BRAND}, + * {@link Build#DEVICE}, {@link Build#MANUFACTURER}, {@link Build#MODEL}, + * {@link Build#PRODUCT}). The attestation certificate chain can + * be retrieved with {@link java.security.KeyStore#getCertificateChain(String)}. + * + *

If {@code attestationChallenge} is {@code null}, the public key certificate for + * this key pair will not contain the extension with the requested attested values. + * + *

{@link javax.crypto.KeyGenerator#generateKey()} will throw + * {@link java.security.ProviderException} if device properties attestation fails or is not + * supported. + */ + @NonNull + public Builder setDevicePropertiesAttestationIncluded( + boolean devicePropertiesAttestationIncluded) { + mDevicePropertiesAttestationIncluded = devicePropertiesAttestationIncluded; + return this; + } + /** * @hide Only system apps can use this method. * @@ -1463,6 +1510,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUserAuthenticationType, mUserPresenceRequired, mAttestationChallenge, + mDevicePropertiesAttestationIncluded, mUniqueIdIncluded, mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index 9c9773e5d145b..69c15cca68bff 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -100,6 +100,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { out.writeInt(mSpec.getUserAuthenticationType()); out.writeBoolean(mSpec.isUserPresenceRequired()); out.writeByteArray(mSpec.getAttestationChallenge()); + out.writeBoolean(mSpec.isDevicePropertiesAttestationIncluded()); out.writeBoolean(mSpec.isUniqueIdIncluded()); out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody()); out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment()); @@ -157,6 +158,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { final int userAuthenticationTypes = in.readInt(); final boolean userPresenceRequired = in.readBoolean(); final byte[] attestationChallenge = in.createByteArray(); + final boolean devicePropertiesAttestationIncluded = in.readBoolean(); final boolean uniqueIdIncluded = in.readBoolean(); final boolean userAuthenticationValidWhileOnBody = in.readBoolean(); final boolean invalidatedByBiometricEnrollment = in.readBoolean(); @@ -190,6 +192,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { userAuthenticationTypes, userPresenceRequired, attestationChallenge, + devicePropertiesAttestationIncluded, uniqueIdIncluded, userAuthenticationValidWhileOnBody, invalidatedByBiometricEnrollment,