From 0306b1ced8325ebb4d7e3b31bd1e183e88a21688 Mon Sep 17 00:00:00 2001 From: Shawn Willden Date: Fri, 5 Mar 2021 10:43:38 -0700 Subject: Modify AttestationUtils to use public Keystore API AttestationUtils calls directly into keystore1 to generate ID attesations. This needs to change prior to keystore2 being enabled and keystore1 deleted. This CL changes the AttestationUtils to use the public API (and one SystemAPI method) to generate ID attestations, allowing the lower layers to handle the transition between keystore1 and keystore2. Test: CtsKeystoreTestCases Change-Id: I64a230b9983cc90767a60d6e7cf2abcf5dfb0108 --- .../security/keystore/AttestationUtils.java | 53 +++++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'keystore') diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java index f1eea820829c..11c36893d984 100644 --- a/keystore/java/android/security/keystore/AttestationUtils.java +++ b/keystore/java/android/security/keystore/AttestationUtils.java @@ -23,7 +23,6 @@ import android.annotation.SystemApi; import android.content.Context; import android.content.res.Resources; import android.os.Build; -import android.security.KeyStore; import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterCertificateChain; import android.security.keymaster.KeymasterDefs; @@ -34,9 +33,14 @@ import android.util.ArraySet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.SecureRandom; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.spec.ECGenParameterSpec; import java.util.Collection; +import java.util.Random; import java.util.Set; /** @@ -256,22 +260,47 @@ public abstract class AttestationUtils { @NonNull public static X509Certificate[] attestDeviceIds(Context context, @NonNull int[] idTypes, @NonNull byte[] attestationChallenge) throws DeviceIdAttestationException { - final KeymasterArguments attestArgs = prepareAttestationArgumentsForDeviceId( - context, idTypes, attestationChallenge); + String keystoreAlias = generateRandomAlias(); + KeyGenParameterSpec.Builder builder = + new KeyGenParameterSpec.Builder(keystoreAlias, KeyProperties.PURPOSE_SIGN) + .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1")) + .setDigests(KeyProperties.DIGEST_SHA256) + .setAttestationChallenge(attestationChallenge); - // Perform attestation. - final KeymasterCertificateChain outChain = new KeymasterCertificateChain(); - final int errorCode = KeyStore.getInstance().attestDeviceIds(attestArgs, outChain); - if (errorCode != KeyStore.NO_ERROR) { - throw new DeviceIdAttestationException("Unable to perform attestation", - KeyStore.getKeyStoreException(errorCode)); + if (idTypes != null) { + builder.setAttestationIds(idTypes); + builder.setDevicePropertiesAttestationIncluded(true); } try { - return parseCertificateChain(outChain); - } catch (KeyAttestationException e) { - throw new DeviceIdAttestationException(e.getMessage(), e); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); + keyPairGenerator.initialize(builder.build()); + keyPairGenerator.generateKeyPair(); + + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + + X509Certificate[] certificateChain = + (X509Certificate[]) keyStore.getCertificateChain(keystoreAlias); + + keyStore.deleteEntry(keystoreAlias); + + return certificateChain; + } catch (Exception e) { + throw new DeviceIdAttestationException("Unable to perform attestation", e); + } + } + + private static String generateRandomAlias() { + Random random = new SecureRandom(); + StringBuilder builder = new StringBuilder(); + // Pick random uppercase letters, A-Z. 20 of them gives us ~94 bits of entropy, which + // should prevent any conflicts with app-selected aliases, even for very unlucky users. + for (int i = 0; i < 20; ++i) { + builder.append(random.nextInt(26) + 'A'); } + return builder.toString(); } /** -- cgit v1.2.3