diff options
author | Janis Danisevskis <jdanis@google.com> | 2020-10-15 15:42:00 -0700 |
---|---|---|
committer | Janis Danisevskis <jdanis@google.com> | 2020-11-13 19:55:41 -0800 |
commit | 940e05164eb880809575eb0b08ffbd38fa67dc89 (patch) | |
tree | f983f54a301682d03e75fb43b2d4e9c1be8fa4f4 | |
parent | e6495d774b4e310aed99820cfe5a6ea731a9d2fb (diff) |
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
-rw-r--r-- | keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java | 186 | ||||
-rw-r--r-- | keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java | 439 |
2 files changed, 285 insertions, 340 deletions
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java index 7d18be553122..ccd0a4bf92ff 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<KeyParameter> 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)); - } + default: + throw new ProviderException("Keystore key generation failed", e); } - @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA; + } + @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA; + try { + keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( + mKeymasterAlgorithm, mKeymasterDigest); + } catch (IllegalArgumentException e) { 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()); + 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 ff40c1586212..a747a0e727d8 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); + + KeyMetadata metadata = iSecurityLevel.generateKey(descriptor, null, + constructKeyGenerationArguments(), flags, additionalEntropy); - storeCertificateChain(flags, createCertificateChain(privateKeyAlias, keyPair)); + 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<byte[]> createCertificateChain(final String privateKeyAlias, KeyPair keyPair) + private void addAttestationParameters(@NonNull List<KeyParameter> 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<KeyParameter> constructKeyGenerationArguments() { + List<KeyParameter> 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() + )); } - } - - 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<byte[]> iterable) - throws ProviderException { - Iterator<byte[]> iter = iterable.iterator(); - storeCertificate( - Credentials.USER_CERTIFICATE, iter.next(), flags, "Failed to store certificate"); - - if (!iter.hasNext()) { - return; + if (mSpec.getKeyValidityForOriginationEnd() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, + mSpec.getKeyValidityForOriginationEnd() + )); } - - ByteArrayOutputStream certificateConcatenationStream = new ByteArrayOutputStream(); - while (iter.hasNext()) { - byte[] data = iter.next(); - certificateConcatenationStream.write(data, 0, data.length); + if (mSpec.getKeyValidityForConsumptionEnd() != null) { + params.add(KeyStore2ParameterUtils.makeDate( + KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, + mSpec.getKeyValidityForConsumptionEnd() + )); } - storeCertificate(Credentials.CA_CERTIFICATE, certificateConcatenationStream.toByteArray(), - flags, "Failed to store attestation CA certificate"); - } + addAlgorithmSpecificParameters(params); - 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)); + if (mSpec.isUniqueIdIncluded()) { + params.add(KeyStore2ParameterUtils.makeBool(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID)); } - } - 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); - } - } + addAttestationParameters(params); - private Iterable<byte[]> 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<byte[]> chain = outChain.getCertificates(); - if (chain.size() < 2) { - throw new ProviderException("Attestation certificate chain contained " - + chain.size() + " entries. At least two are required."); - } - return chain; + return params; } - private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) { + private void addAlgorithmSpecificParameters(List<KeyParameter> 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: |