From 940e05164eb880809575eb0b08ffbd38fa67dc89 Mon Sep 17 00:00:00 2001 From: Janis Danisevskis Date: Thu, 15 Oct 2020 15:42:00 -0700 Subject: [PATCH] Keystore 2.0 SPI: Evolve the generator SPI. We delegate the generation of self signed certificates to the KeyMint backend. Also we use the KeyParamter AIDL type instead of KeymasterArguments to construct parameter lists. Bug: 159476414 Test: None Change-Id: I441a4d4df4ef04e3da8aeaff3274c609d549c979 --- .../AndroidKeyStoreKeyGeneratorSpi.java | 190 +++++--- .../AndroidKeyStoreKeyPairGeneratorSpi.java | 453 +++++++----------- 2 files changed, 294 insertions(+), 349 deletions(-) diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java index 7d18be5531227..ccd0a4bf92ffb 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java @@ -16,15 +16,22 @@ package android.security.keystore2; -import android.security.Credentials; -import android.security.KeyStore; -import android.security.keymaster.KeyCharacteristics; +import android.security.KeyStore2; +import android.security.KeyStoreSecurityLevel; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterDefs; +import android.security.keystore.ArrayUtils; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.security.keystore.KeymasterUtils; import android.security.keystore.StrongBoxUnavailableException; +import android.system.keystore2.Domain; +import android.system.keystore2.IKeystoreSecurityLevel; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyMetadata; +import android.system.keystore2.KeyParameter; +import android.system.keystore2.SecurityLevel; +import android.util.Log; import libcore.util.EmptyArray; @@ -32,7 +39,9 @@ import java.security.InvalidAlgorithmParameterException; import java.security.ProviderException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import javax.crypto.KeyGeneratorSpi; import javax.crypto.SecretKey; @@ -43,6 +52,7 @@ import javax.crypto.SecretKey; * @hide */ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { + private static final String TAG = "AndroidKeyStoreKeyGeneratorSpi"; public static class AES extends AndroidKeyStoreKeyGeneratorSpi { public AES() { @@ -105,7 +115,7 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } } - private final KeyStore mKeyStore = KeyStore.getInstance(); + private final KeyStore2 mKeyStore = KeyStore2.getInstance(); private final int mKeymasterAlgorithm; private final int mKeymasterDigest; private final int mDefaultKeySizeBits; @@ -278,77 +288,141 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { throw new IllegalStateException("Not initialized"); } - KeymasterArguments args = new KeymasterArguments(); - args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits); - args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); - args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes); - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes); - args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings); - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); - KeymasterUtils.addUserAuthArgs(args, spec); - KeymasterUtils.addMinMacLengthAuthorizationIfNecessary( - args, - mKeymasterAlgorithm, - mKeymasterBlockModes, - mKeymasterDigests); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, - spec.getKeyValidityForOriginationEnd()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, - spec.getKeyValidityForConsumptionEnd()); + List params = new ArrayList<>(); + + params.add(KeyStore2ParameterUtils.makeInt( + KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits + )); + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm + )); + ArrayUtils.forEach(mKeymasterPurposes, (purpose) -> { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_PURPOSE, purpose + )); + }); + ArrayUtils.forEach(mKeymasterBlockModes, (blockMode) -> { + if (blockMode == KeymasterDefs.KM_MODE_GCM + && mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES) { + params.add(KeyStore2ParameterUtils.makeInt( + KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, + AndroidKeyStoreAuthenticatedAESCipherSpi.GCM + .MIN_SUPPORTED_TAG_LENGTH_BITS + )); + } + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_BLOCK_MODE, blockMode + )); + }); + ArrayUtils.forEach(mKeymasterPaddings, (padding) -> { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_PADDING, padding + )); + }); + ArrayUtils.forEach(mKeymasterDigests, (digest) -> { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_DIGEST, digest + )); + }); + + if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC + && mKeymasterDigests.length != 0) { + int digestOutputSizeBits = KeymasterUtils.getDigestOutputSizeBits(mKeymasterDigests[0]); + if (digestOutputSizeBits == -1) { + throw new ProviderException( + "HMAC key authorized for unsupported digest: " + + KeyProperties.Digest.fromKeymaster(mKeymasterDigests[0])); + } + params.add(KeyStore2ParameterUtils.makeInt( + KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits + )); + } + + KeyStore2ParameterUtils.addUserAuthArgs(params, spec); + + if (spec.getKeyValidityStart() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart() + )); + } + if (spec.getKeyValidityForOriginationEnd() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, + spec.getKeyValidityForOriginationEnd() + )); + } + if (spec.getKeyValidityForConsumptionEnd() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, + spec.getKeyValidityForConsumptionEnd() + )); + } if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) && (!spec.isRandomizedEncryptionRequired())) { // Permit caller-provided IV when encrypting with this key - args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE); + params.add(KeyStore2ParameterUtils.makeBool( + KeymasterDefs.KM_TAG_CALLER_NONCE + )); } byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( mRng, (mKeySizeBits + 7) / 8); - int flags = 0; + + @SecurityLevel int securityLevel = SecurityLevel.TRUSTED_ENVIRONMENT; if (spec.isStrongBoxBacked()) { - flags |= KeyStore.FLAG_STRONGBOX; + securityLevel = SecurityLevel.STRONGBOX; } + + int flags = 0; if (spec.isCriticalToDeviceEncryption()) { - flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; + flags |= IKeystoreSecurityLevel.KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING; } - String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias(); - KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); - boolean success = false; + + KeyDescriptor descriptor = new KeyDescriptor(); + descriptor.alias = spec.getKeystoreAlias(); + descriptor.nspace = spec.getNamespace(); + descriptor.domain = descriptor.nspace == KeyProperties.NAMESPACE_APPLICATION + ? Domain.APP + : Domain.SELINUX; + descriptor.blob = null; + + KeyMetadata metadata = null; + KeyStoreSecurityLevel iSecurityLevel = null; try { - Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias(), spec.getUid()); - int errorCode = mKeyStore.generateKey( - keyAliasInKeystore, - args, - additionalEntropy, - spec.getUid(), + iSecurityLevel = mKeyStore.getSecurityLevel(securityLevel); + metadata = iSecurityLevel.generateKey( + descriptor, + null, /* Attestation key not applicable to symmetric keys. */ + params, flags, - resultingKeyCharacteristics); - if (errorCode != KeyStore.NO_ERROR) { - if (errorCode == KeyStore.HARDWARE_TYPE_UNAVAILABLE) { + additionalEntropy); + } catch (android.security.KeyStoreException e) { + switch (e.getErrorCode()) { + // TODO replace with ErrorCode.HARDWARE_TYPE_UNAVAILABLE when KeyMint spec + // becomes available. + case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE: throw new StrongBoxUnavailableException("Failed to generate key"); - } else { - throw new ProviderException( - "Keystore operation failed", KeyStore.getKeyStoreException(errorCode)); - } - } - @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA; - try { - keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( - mKeymasterAlgorithm, mKeymasterDigest); - } catch (IllegalArgumentException e) { - throw new ProviderException("Failed to obtain JCA secret key algorithm name", e); - } - SecretKey result = new AndroidKeyStoreSecretKey( - keyAliasInKeystore, spec.getUid(), keyAlgorithmJCA); - success = true; - return result; - } finally { - if (!success) { - Credentials.deleteAllTypesForAlias( - mKeyStore, spec.getKeystoreAlias(), spec.getUid()); + default: + throw new ProviderException("Keystore key generation failed", e); } } + @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA; + try { + keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( + mKeymasterAlgorithm, mKeymasterDigest); + } catch (IllegalArgumentException e) { + try { + mKeyStore.deleteKey(descriptor); + } catch (android.security.KeyStoreException kse) { + Log.e(TAG, "Failed to delete key after generating successfully but" + + " failed to get the algorithm string.", kse); + } + throw new ProviderException("Failed to obtain JCA secret key algorithm name", e); + } + SecretKey result = new AndroidKeyStoreSecretKey(descriptor, metadata, keyAlgorithmJCA, + iSecurityLevel); + return result; } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index ff40c15862128..a747a0e727d87 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -16,58 +16,41 @@ package android.security.keystore2; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Build; -import android.security.Credentials; import android.security.KeyPairGeneratorSpec; -import android.security.KeyStore; -import android.security.keymaster.KeyCharacteristics; +import android.security.KeyStore2; +import android.security.KeyStoreException; +import android.security.KeyStoreSecurityLevel; import android.security.keymaster.KeymasterArguments; -import android.security.keymaster.KeymasterCertificateChain; import android.security.keymaster.KeymasterDefs; +import android.security.keystore.ArrayUtils; import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyPermanentlyInvalidatedException; +import android.security.keystore.KeyProperties; import android.security.keystore.KeymasterUtils; import android.security.keystore.SecureKeyImportUnavailableException; import android.security.keystore.StrongBoxUnavailableException; - -import com.android.org.bouncycastle.asn1.ASN1EncodableVector; -import com.android.org.bouncycastle.asn1.ASN1InputStream; -import com.android.org.bouncycastle.asn1.ASN1Integer; -import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier; -import com.android.org.bouncycastle.asn1.DERBitString; -import com.android.org.bouncycastle.asn1.DERNull; -import com.android.org.bouncycastle.asn1.DERSequence; -import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import com.android.org.bouncycastle.asn1.x509.Certificate; -import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import com.android.org.bouncycastle.asn1.x509.TBSCertificate; -import com.android.org.bouncycastle.asn1.x509.Time; -import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; -import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers; -import com.android.org.bouncycastle.jce.X509Principal; -import com.android.org.bouncycastle.jce.provider.X509CertificateObject; -import com.android.org.bouncycastle.x509.X509V3CertificateGenerator; +import android.system.keystore2.Domain; +import android.system.keystore2.IKeystoreSecurityLevel; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyMetadata; +import android.system.keystore2.KeyParameter; +import android.system.keystore2.ResponseCode; +import android.system.keystore2.SecurityLevel; +import android.util.Log; 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; import java.security.KeyPairGeneratorSpi; -import java.security.PrivateKey; import java.security.ProviderException; -import java.security.PublicKey; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateEncodingException; -import java.security.cert.CertificateParsingException; -import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; import java.security.spec.RSAKeyGenParameterSpec; @@ -76,7 +59,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -96,6 +78,7 @@ import java.util.Set; * @hide */ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGeneratorSpi { + private static final String TAG = "AndroidKeyStoreKeyPairGeneratorSpi"; public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi { public RSA() { @@ -154,13 +137,12 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private final int mOriginalKeymasterAlgorithm; - private KeyStore mKeyStore; + private KeyStore2 mKeyStore; private KeyGenParameterSpec mSpec; private String mEntryAlias; private int mEntryUid; - private boolean mEncryptionAtRestRequired; private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm; private int mKeymasterAlgorithm = -1; private int mKeySizeBits; @@ -172,7 +154,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private int[] mKeymasterSignaturePaddings; private int[] mKeymasterDigests; - private BigInteger mRSAPublicExponent; + private Long mRSAPublicExponent; protected AndroidKeyStoreKeyPairGeneratorSpi(int keymasterAlgorithm) { mOriginalKeymasterAlgorithm = keymasterAlgorithm; @@ -283,7 +265,6 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber()); specBuilder.setCertificateNotBefore(legacySpec.getStartDate()); specBuilder.setCertificateNotAfter(legacySpec.getEndDate()); - encryptionAtRestRequired = legacySpec.isEncryptionRequired(); specBuilder.setUserAuthenticationRequired(false); spec = specBuilder.build(); @@ -301,7 +282,6 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mEntryUid = spec.getUid(); mSpec = spec; mKeymasterAlgorithm = keymasterAlgorithm; - mEncryptionAtRestRequired = encryptionAtRestRequired; mKeySizeBits = spec.getKeySize(); initAlgorithmSpecificParameters(); if (mKeySizeBits == -1) { @@ -355,7 +335,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mJcaKeyAlgorithm = jcaKeyAlgorithm; mRng = random; - mKeyStore = KeyStore.getInstance(); + mKeyStore = KeyStore2.getInstance(); success = true; } finally { if (!success) { @@ -366,7 +346,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private void resetAll() { mEntryAlias = null; - mEntryUid = KeyStore.UID_SELF; + mEntryUid = KeyProperties.NAMESPACE_APPLICATION; mJcaKeyAlgorithm = null; mKeymasterAlgorithm = -1; mKeymasterPurposes = null; @@ -377,7 +357,6 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mKeySizeBits = 0; mSpec = null; mRSAPublicExponent = null; - mEncryptionAtRestRequired = false; mRng = null; mKeyStore = null; } @@ -409,12 +388,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato throw new InvalidAlgorithmParameterException( "RSA public exponent must be positive: " + publicExponent); } - if (publicExponent.compareTo(KeymasterArguments.UINT64_MAX_VALUE) > 0) { + if ((publicExponent.signum() == -1) + || (publicExponent.compareTo(KeymasterArguments.UINT64_MAX_VALUE) > 0)) { throw new InvalidAlgorithmParameterException( "Unsupported RSA public exponent: " + publicExponent + ". Maximum supported value: " + KeymasterArguments.UINT64_MAX_VALUE); } - mRSAPublicExponent = publicExponent; + mRSAPublicExponent = publicExponent.longValue(); break; } case KeymasterDefs.KM_ALGORITHM_EC: @@ -451,204 +431,178 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato throw new IllegalStateException("Not initialized"); } - int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0; - if (((flags & KeyStore.FLAG_ENCRYPTED) != 0) - && (mKeyStore.state() != KeyStore.State.UNLOCKED)) { - throw new IllegalStateException( - "Encryption at rest using secure lock screen credential requested for key pair" - + ", but the user has not yet entered the credential"); - } + final @SecurityLevel int securityLevel = + mSpec.isStrongBoxBacked() + ? SecurityLevel.STRONGBOX + : SecurityLevel.TRUSTED_ENVIRONMENT; - if (mSpec.isStrongBoxBacked()) { - flags |= KeyStore.FLAG_STRONGBOX; - } - if (mSpec.isCriticalToDeviceEncryption()) { - flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION; - } + final int flags = + mSpec.isCriticalToDeviceEncryption() + ? IKeystoreSecurityLevel + .KEY_FLAG_AUTH_BOUND_WITHOUT_CRYPTOGRAPHIC_LSKF_BINDING + : 0; byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( mRng, (mKeySizeBits + 7) / 8); - Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid); - final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias; + KeyDescriptor descriptor = new KeyDescriptor(); + descriptor.alias = mEntryAlias; + descriptor.domain = mEntryUid == KeyProperties.NAMESPACE_APPLICATION + ? Domain.APP + : Domain.SELINUX; + descriptor.nspace = mEntryUid; + descriptor.blob = null; + boolean success = false; try { - generateKeystoreKeyPair( - privateKeyAlias, constructKeyGenerationArguments(), additionalEntropy, flags); - KeyPair keyPair = loadKeystoreKeyPair(privateKeyAlias); + KeyStoreSecurityLevel iSecurityLevel = mKeyStore.getSecurityLevel(securityLevel); - storeCertificateChain(flags, createCertificateChain(privateKeyAlias, keyPair)); + KeyMetadata metadata = iSecurityLevel.generateKey(descriptor, null, + constructKeyGenerationArguments(), flags, additionalEntropy); + + AndroidKeyStorePublicKey publicKey = + AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse( + descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm); success = true; - return keyPair; - } catch (ProviderException e) { - if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { - throw new SecureKeyImportUnavailableException(e); - } else { - throw e; - } + return new KeyPair(publicKey, publicKey.getPrivateKey()); + } catch (android.security.KeyStoreException e) { + switch(e.getErrorCode()) { + case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE: + throw new StrongBoxUnavailableException("Failed to generated key pair.", e); + default: + ProviderException p = new ProviderException("Failed to generate key pair.", e); + if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { + throw new SecureKeyImportUnavailableException(p); + } + throw p; + } + } catch (UnrecoverableKeyException e) { + throw new ProviderException( + "Failed to construct key object from newly generated key pair.", e); } finally { if (!success) { - Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid); + try { + mKeyStore.deleteKey(descriptor); + } catch (KeyStoreException e) { + if (e.getErrorCode() != ResponseCode.KEY_NOT_FOUND) { + Log.e(TAG, "Failed to delete newly generated key after " + + "generation failed unexpectedly.", e); + } + } } } } - private Iterable createCertificateChain(final String privateKeyAlias, KeyPair keyPair) + private void addAttestationParameters(@NonNull List params) throws ProviderException { byte[] challenge = mSpec.getAttestationChallenge(); + if (challenge != null) { - KeymasterArguments args = new KeymasterArguments(); - args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge); + params.add(KeyStore2ParameterUtils.makeBytes( + 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)); + params.add(KeyStore2ParameterUtils.makeBytes( + KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND, + Build.BRAND.getBytes(StandardCharsets.UTF_8) + )); + params.add(KeyStore2ParameterUtils.makeBytes( + KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE, + Build.DEVICE.getBytes(StandardCharsets.UTF_8) + )); + params.add(KeyStore2ParameterUtils.makeBytes( + KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT, + Build.PRODUCT.getBytes(StandardCharsets.UTF_8) + )); + params.add(KeyStore2ParameterUtils.makeBytes( + KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER, + Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8) + )); + params.add(KeyStore2ParameterUtils.makeBytes( + KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL, + Build.MODEL.getBytes(StandardCharsets.UTF_8) + )); } - - return getAttestationChain(privateKeyAlias, keyPair, args); - } - - // Very short certificate chain in the non-attestation case. - return Collections.singleton(generateSelfSignedCertificateBytes(keyPair)); - } - - private void generateKeystoreKeyPair(final String privateKeyAlias, KeymasterArguments args, - byte[] additionalEntropy, final int flags) throws ProviderException { - KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics(); - int errorCode = mKeyStore.generateKey(privateKeyAlias, args, additionalEntropy, - mEntryUid, flags, resultingKeyCharacteristics); - if (errorCode != KeyStore.NO_ERROR) { - 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)); + } else { + if (mSpec.isDevicePropertiesAttestationIncluded()) { + throw new ProviderException("An attestation challenge must be provided when " + + "requesting device properties attestation."); } } } - private KeyPair loadKeystoreKeyPair(final String privateKeyAlias) throws ProviderException { - try { - KeyPair result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore( - mKeyStore, privateKeyAlias, mEntryUid); - if (!mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) { - throw new ProviderException( - "Generated key pair algorithm does not match requested algorithm: " - + result.getPrivate().getAlgorithm() + " vs " + mJcaKeyAlgorithm); - } - return result; - } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { - throw new ProviderException("Failed to load generated key pair from keystore", e); + private Collection constructKeyGenerationArguments() { + List params = new ArrayList<>(); + params.add(KeyStore2ParameterUtils.makeInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits)); + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm + )); + ArrayUtils.forEach(mKeymasterPurposes, (purpose) -> { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_PURPOSE, purpose + )); + }); + ArrayUtils.forEach(mKeymasterBlockModes, (blockMode) -> { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_BLOCK_MODE, blockMode + )); + }); + ArrayUtils.forEach(mKeymasterEncryptionPaddings, (padding) -> { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_PADDING, padding + )); + }); + ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_PADDING, padding + )); + }); + ArrayUtils.forEach(mKeymasterDigests, (digest) -> { + params.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_DIGEST, digest + )); + }); + + KeyStore2ParameterUtils.addUserAuthArgs(params, mSpec); + + if (mSpec.getKeyValidityStart() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart() + )); } + if (mSpec.getKeyValidityForOriginationEnd() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, + mSpec.getKeyValidityForOriginationEnd() + )); + } + if (mSpec.getKeyValidityForConsumptionEnd() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, + mSpec.getKeyValidityForConsumptionEnd() + )); + } + + addAlgorithmSpecificParameters(params); + + if (mSpec.isUniqueIdIncluded()) { + params.add(KeyStore2ParameterUtils.makeBool(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID)); + } + + addAttestationParameters(params); + + return params; } - private KeymasterArguments constructKeyGenerationArguments() { - KeymasterArguments args = new KeymasterArguments(); - args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits); - args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm); - args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes); - args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes); - args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterEncryptionPaddings); - args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings); - args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests); - - KeymasterUtils.addUserAuthArgs(args, mSpec); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, - mSpec.getKeyValidityForOriginationEnd()); - args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, - mSpec.getKeyValidityForConsumptionEnd()); - addAlgorithmSpecificParameters(args); - - if (mSpec.isUniqueIdIncluded()) - args.addBoolean(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID); - - return args; - } - - private void storeCertificateChain(final int flags, Iterable iterable) - throws ProviderException { - Iterator iter = iterable.iterator(); - storeCertificate( - Credentials.USER_CERTIFICATE, iter.next(), flags, "Failed to store certificate"); - - if (!iter.hasNext()) { - return; - } - - ByteArrayOutputStream certificateConcatenationStream = new ByteArrayOutputStream(); - while (iter.hasNext()) { - byte[] data = iter.next(); - certificateConcatenationStream.write(data, 0, data.length); - } - - storeCertificate(Credentials.CA_CERTIFICATE, certificateConcatenationStream.toByteArray(), - flags, "Failed to store attestation CA certificate"); - } - - private void storeCertificate(String prefix, byte[] certificateBytes, final int flags, - String failureMessage) throws ProviderException { - int insertErrorCode = mKeyStore.insert( - prefix + mEntryAlias, - certificateBytes, - mEntryUid, - flags); - if (insertErrorCode != KeyStore.NO_ERROR) { - throw new ProviderException(failureMessage, - KeyStore.getKeyStoreException(insertErrorCode)); - } - } - - private byte[] generateSelfSignedCertificateBytes(KeyPair keyPair) throws ProviderException { - try { - return generateSelfSignedCertificate(keyPair.getPrivate(), keyPair.getPublic()) - .getEncoded(); - } catch (IOException | CertificateParsingException e) { - throw new ProviderException("Failed to generate self-signed certificate", e); - } catch (CertificateEncodingException e) { - throw new ProviderException( - "Failed to obtain encoded form of self-signed certificate", e); - } - } - - private Iterable getAttestationChain(String privateKeyAlias, - KeyPair keyPair, KeymasterArguments args) - throws ProviderException { - 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)); - } - Collection chain = outChain.getCertificates(); - if (chain.size() < 2) { - throw new ProviderException("Attestation certificate chain contained " - + chain.size() + " entries. At least two are required."); - } - return chain; - } - - private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) { + private void addAlgorithmSpecificParameters(List params) { switch (mKeymasterAlgorithm) { case KeymasterDefs.KM_ALGORITHM_RSA: - keymasterArgs.addUnsignedLong( - KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent); + params.add(KeyStore2ParameterUtils.makeLong( + KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent + )); break; case KeymasterDefs.KM_ALGORITHM_EC: break; @@ -657,89 +611,6 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } } - private X509Certificate generateSelfSignedCertificate(PrivateKey privateKey, - PublicKey publicKey) throws CertificateParsingException, IOException { - String signatureAlgorithm = - getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec); - if (signatureAlgorithm == null) { - // Key cannot be used to sign a certificate - return generateSelfSignedCertificateWithFakeSignature(publicKey); - } else { - // Key can be used to sign a certificate - try { - return generateSelfSignedCertificateWithValidSignature( - privateKey, publicKey, signatureAlgorithm); - } catch (Exception e) { - // Failed to generate the self-signed certificate with valid signature. Fall back - // to generating a self-signed certificate with a fake signature. This is done for - // all exception types because we prefer key pair generation to succeed and end up - // producing a self-signed certificate with an invalid signature to key pair - // generation failing. - return generateSelfSignedCertificateWithFakeSignature(publicKey); - } - } - } - - @SuppressWarnings("deprecation") - private X509Certificate generateSelfSignedCertificateWithValidSignature( - PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm) throws Exception { - final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); - certGen.setPublicKey(publicKey); - certGen.setSerialNumber(mSpec.getCertificateSerialNumber()); - certGen.setSubjectDN(mSpec.getCertificateSubject()); - certGen.setIssuerDN(mSpec.getCertificateSubject()); - certGen.setNotBefore(mSpec.getCertificateNotBefore()); - certGen.setNotAfter(mSpec.getCertificateNotAfter()); - certGen.setSignatureAlgorithm(signatureAlgorithm); - return certGen.generate(privateKey); - } - - @SuppressWarnings("deprecation") - private X509Certificate generateSelfSignedCertificateWithFakeSignature( - PublicKey publicKey) throws IOException, CertificateParsingException { - V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator(); - ASN1ObjectIdentifier sigAlgOid; - AlgorithmIdentifier sigAlgId; - byte[] signature; - switch (mKeymasterAlgorithm) { - case KeymasterDefs.KM_ALGORITHM_EC: - sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256; - sigAlgId = new AlgorithmIdentifier(sigAlgOid); - ASN1EncodableVector v = new ASN1EncodableVector(); - v.add(new ASN1Integer(BigInteger.valueOf(0))); - v.add(new ASN1Integer(BigInteger.valueOf(0))); - signature = new DERSequence().getEncoded(); - break; - case KeymasterDefs.KM_ALGORITHM_RSA: - sigAlgOid = PKCSObjectIdentifiers.sha256WithRSAEncryption; - sigAlgId = new AlgorithmIdentifier(sigAlgOid, DERNull.INSTANCE); - signature = new byte[1]; - break; - default: - throw new ProviderException("Unsupported key algorithm: " + mKeymasterAlgorithm); - } - - try (ASN1InputStream publicKeyInfoIn = new ASN1InputStream(publicKey.getEncoded())) { - tbsGenerator.setSubjectPublicKeyInfo( - SubjectPublicKeyInfo.getInstance(publicKeyInfoIn.readObject())); - } - tbsGenerator.setSerialNumber(new ASN1Integer(mSpec.getCertificateSerialNumber())); - X509Principal subject = - new X509Principal(mSpec.getCertificateSubject().getEncoded()); - tbsGenerator.setSubject(subject); - tbsGenerator.setIssuer(subject); - tbsGenerator.setStartDate(new Time(mSpec.getCertificateNotBefore())); - tbsGenerator.setEndDate(new Time(mSpec.getCertificateNotAfter())); - tbsGenerator.setSignature(sigAlgId); - TBSCertificate tbsCertificate = tbsGenerator.generateTBSCertificate(); - - ASN1EncodableVector result = new ASN1EncodableVector(); - result.add(tbsCertificate); - result.add(sigAlgId); - result.add(new DERBitString(signature)); - return new X509CertificateObject(Certificate.getInstance(new DERSequence(result))); - } - private static int getDefaultKeySize(int keymasterAlgorithm) { switch (keymasterAlgorithm) { case KeymasterDefs.KM_ALGORITHM_EC: