Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84cd6f22 authored by Max Bires's avatar Max Bires Committed by Janis Danisevskis
Browse files

Adding device ID attestation to KeyGenParameterSpec

Now that attestation and generation of keys occurs in the same step, the
device ID attestation parameters need to be passed into the
KeyPairGeneratorSpi. This change shifts functionality that was
previously in AttestationUtils into KeyGenParameterSpec and the
keystore2 KeyPairGeneratorSpi. The API changes should be gated to
Platform APIs and hidden from less privileged components.

Test: atest cts/tests/tests/keystore/src/android/keystore/cts/KeyGenParameterSpecTest.java
Bug: 177369988
Change-Id: Iafbc1661583bdf61da644b2c0838b9024018ee82
parent 84da5781
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -8427,10 +8427,12 @@ package android.security.keystore {
  }
  public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
    method @Nullable public int[] getAttestationIds();
    method public int getNamespace();
  }
  public static final class KeyGenParameterSpec.Builder {
    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setAttestationIds(@NonNull int[]);
    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setNamespace(int);
    method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
  }
+64 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.security.keystore;

import android.annotation.Nullable;
import android.content.Context;
import android.os.Build;
import android.security.Credentials;
import android.security.KeyPairGeneratorSpec;
@@ -25,6 +26,8 @@ import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
import android.telephony.TelephonyManager;
import android.util.ArraySet;

import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
@@ -477,11 +480,11 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato

            success = true;
            return keyPair;
        } catch (ProviderException e) {
        } catch (ProviderException | IllegalArgumentException | DeviceIdAttestationException e) {
          if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
              throw new SecureKeyImportUnavailableException(e);
          } else {
              throw e;
                throw new ProviderException(e);
          }
        } finally {
            if (!success) {
@@ -491,7 +494,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
    }

    private Iterable<byte[]> createCertificateChain(final String privateKeyAlias, KeyPair keyPair)
            throws ProviderException {
            throws ProviderException, DeviceIdAttestationException {
        byte[] challenge = mSpec.getAttestationChallenge();
        if (challenge != null) {
            KeymasterArguments args = new KeymasterArguments();
@@ -510,6 +513,60 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
                        Build.MODEL.getBytes(StandardCharsets.UTF_8));
            }

            int[] idTypes = mSpec.getAttestationIds();
            if (idTypes != null) {
                final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
                for (int idType : idTypes) {
                    idTypesSet.add(idType);
                }
                TelephonyManager telephonyService = null;
                if (idTypesSet.contains(AttestationUtils.ID_TYPE_IMEI)
                        || idTypesSet.contains(AttestationUtils.ID_TYPE_MEID)) {
                    telephonyService =
                            (TelephonyManager) KeyStore.getApplicationContext().getSystemService(
                                    Context.TELEPHONY_SERVICE);
                    if (telephonyService == null) {
                        throw new DeviceIdAttestationException(
                                "Unable to access telephony service");
                    }
                }
                for (final Integer idType : idTypesSet) {
                    switch (idType) {
                        case AttestationUtils.ID_TYPE_SERIAL:
                            args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL,
                                    Build.getSerial().getBytes(StandardCharsets.UTF_8)
                            );
                            break;
                        case AttestationUtils.ID_TYPE_IMEI: {
                            final String imei = telephonyService.getImei(0);
                            if (imei == null) {
                                throw new DeviceIdAttestationException("Unable to retrieve IMEI");
                            }
                            args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_IMEI,
                                    imei.getBytes(StandardCharsets.UTF_8)
                            );
                            break;
                        }
                        case AttestationUtils.ID_TYPE_MEID: {
                            final String meid = telephonyService.getMeid(0);
                            if (meid == null) {
                                throw new DeviceIdAttestationException("Unable to retrieve MEID");
                            }
                            args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID,
                                    meid.getBytes(StandardCharsets.UTF_8)
                            );
                            break;
                        }
                        case AttestationUtils.USE_INDIVIDUAL_ATTESTATION: {
                            args.addBoolean(KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION);
                            break;
                        }
                        default:
                            throw new IllegalArgumentException("Unknown device ID type " + idType);
                    }
                }
            }

            return getAttestationChain(privateKeyAlias, keyPair, args);
        }

@@ -547,7 +604,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
        }
    }

    private KeymasterArguments constructKeyGenerationArguments() {
    private KeymasterArguments constructKeyGenerationArguments()
            throws IllegalArgumentException, DeviceIdAttestationException {
        KeymasterArguments args = new KeymasterArguments();
        args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
@@ -565,9 +623,9 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
                mSpec.getKeyValidityForConsumptionEnd());
        addAlgorithmSpecificParameters(args);

        if (mSpec.isUniqueIdIncluded())
        if (mSpec.isUniqueIdIncluded()) {
            args.addBoolean(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID);

        }
        return args;
    }

+8 −0
Original line number Diff line number Diff line
@@ -34,6 +34,14 @@ public abstract class ArrayUtils {
        return ((array != null) && (array.length > 0)) ? array.clone() : array;
    }

    /**
     * Clones an array if it is not null and has a length greater than 0. Otherwise, returns the
     * array.
     */
    public static int[] cloneIfNotEmpty(int[] array) {
        return ((array != null) && (array.length > 0)) ? array.clone() : array;
    }

    public static byte[] cloneIfNotEmpty(byte[] array) {
        return ((array != null) && (array.length > 0)) ? array.clone() : array;
    }
+45 −0
Original line number Diff line number Diff line
@@ -267,6 +267,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
    private final boolean mUserPresenceRequired;
    private final byte[] mAttestationChallenge;
    private final boolean mDevicePropertiesAttestationIncluded;
    private final int[] mAttestationIds;
    private final boolean mUniqueIdIncluded;
    private final boolean mUserAuthenticationValidWhileOnBody;
    private final boolean mInvalidatedByBiometricEnrollment;
@@ -308,6 +309,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
            boolean userPresenceRequired,
            byte[] attestationChallenge,
            boolean devicePropertiesAttestationIncluded,
            int[] attestationIds,
            boolean uniqueIdIncluded,
            boolean userAuthenticationValidWhileOnBody,
            boolean invalidatedByBiometricEnrollment,
@@ -361,6 +363,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
        mUserAuthenticationType = userAuthenticationType;
        mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
        mDevicePropertiesAttestationIncluded = devicePropertiesAttestationIncluded;
        mAttestationIds = attestationIds;
        mUniqueIdIncluded = uniqueIdIncluded;
        mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
        mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
@@ -719,6 +722,25 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
        return mDevicePropertiesAttestationIncluded;
    }

    /**
     * @hide
     * Allows the caller to specify device IDs to be attested to in the certificate for the
     * generated key pair. These values are the enums specified in
     * {@link android.security.keystore.AttestationUtils}
     *
     * @see android.security.keystore.AttestationUtils#ID_TYPE_SERIAL
     * @see android.security.keystore.AttestationUtils#ID_TYPE_IMEI
     * @see android.security.keystore.AttestationUtils#ID_TYPE_MEID
     * @see android.security.keystore.AttestationUtils#USE_INDIVIDUAL_ATTESTATION
     *
     * @return integer array representing the requested device IDs to attest.
     */
    @SystemApi
    @Nullable
    public int[] getAttestationIds() {
        return Utils.cloneIfNotNull(mAttestationIds);
    }

    /**
     * @hide This is a system-only API
     *
@@ -834,6 +856,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
        private boolean mUserPresenceRequired = false;
        private byte[] mAttestationChallenge = null;
        private boolean mDevicePropertiesAttestationIncluded = false;
        private int[] mAttestationIds = null;
        private boolean mUniqueIdIncluded = false;
        private boolean mUserAuthenticationValidWhileOnBody;
        private boolean mInvalidatedByBiometricEnrollment = true;
@@ -902,6 +925,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
            mAttestationChallenge = sourceSpec.getAttestationChallenge();
            mDevicePropertiesAttestationIncluded =
                    sourceSpec.isDevicePropertiesAttestationIncluded();
            mAttestationIds = sourceSpec.getAttestationIds();
            mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
            mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
            mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment();
@@ -1472,6 +1496,26 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
            return this;
        }

        /**
         * @hide
         * Sets which IDs to attest in the attestation certificate for the key. The acceptable
         * values in this integer array are the enums specified in
         * {@link android.security.keystore.AttestationUtils}
         *
         * @param attestationIds the array of ID types to attest to in the certificate.
         *
         * @see android.security.keystore.AttestationUtils#ID_TYPE_SERIAL
         * @see android.security.keystore.AttestationUtils#ID_TYPE_IMEI
         * @see android.security.keystore.AttestationUtils#ID_TYPE_MEID
         * @see android.security.keystore.AttestationUtils#USE_INDIVIDUAL_ATTESTATION
         */
        @SystemApi
        @NonNull
        public Builder setAttestationIds(@NonNull int[] attestationIds) {
            mAttestationIds = attestationIds;
            return this;
        }

        /**
         * @hide Only system apps can use this method.
         *
@@ -1638,6 +1682,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
                    mUserPresenceRequired,
                    mAttestationChallenge,
                    mDevicePropertiesAttestationIncluded,
                    mAttestationIds,
                    mUniqueIdIncluded,
                    mUserAuthenticationValidWhileOnBody,
                    mInvalidatedByBiometricEnrollment,
+3 −0
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
        out.writeBoolean(mSpec.isUserPresenceRequired());
        out.writeByteArray(mSpec.getAttestationChallenge());
        out.writeBoolean(mSpec.isDevicePropertiesAttestationIncluded());
        out.writeIntArray(mSpec.getAttestationIds());
        out.writeBoolean(mSpec.isUniqueIdIncluded());
        out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
        out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
@@ -160,6 +161,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
        final boolean userPresenceRequired = in.readBoolean();
        final byte[] attestationChallenge = in.createByteArray();
        final boolean devicePropertiesAttestationIncluded = in.readBoolean();
        final int[] attestationIds = in.createIntArray();
        final boolean uniqueIdIncluded = in.readBoolean();
        final boolean userAuthenticationValidWhileOnBody = in.readBoolean();
        final boolean invalidatedByBiometricEnrollment = in.readBoolean();
@@ -195,6 +197,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
                userPresenceRequired,
                attestationChallenge,
                devicePropertiesAttestationIncluded,
                attestationIds,
                uniqueIdIncluded,
                userAuthenticationValidWhileOnBody,
                invalidatedByBiometricEnrollment,
Loading