Merge "Adding device ID attestation to KeyGenParameterSpec"
This commit is contained in:
@@ -8435,10 +8435,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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -33,4 +33,8 @@ abstract class Utils {
|
||||
static byte[] cloneIfNotNull(byte[] value) {
|
||||
return (value != null) ? value.clone() : null;
|
||||
}
|
||||
|
||||
static int[] cloneIfNotNull(int[] value) {
|
||||
return (value != null) ? value.clone() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,16 +18,20 @@ package android.security.keystore2;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.security.keymint.KeyParameter;
|
||||
import android.hardware.security.keymint.SecurityLevel;
|
||||
import android.os.Build;
|
||||
import android.security.KeyPairGeneratorSpec;
|
||||
import android.security.KeyStore;
|
||||
import android.security.KeyStore2;
|
||||
import android.security.KeyStoreException;
|
||||
import android.security.KeyStoreSecurityLevel;
|
||||
import android.security.keymaster.KeymasterArguments;
|
||||
import android.security.keymaster.KeymasterDefs;
|
||||
import android.security.keystore.ArrayUtils;
|
||||
import android.security.keystore.AttestationUtils;
|
||||
import android.security.keystore.DeviceIdAttestationException;
|
||||
import android.security.keystore.KeyGenParameterSpec;
|
||||
import android.security.keystore.KeyProperties;
|
||||
import android.security.keystore.KeymasterUtils;
|
||||
@@ -38,6 +42,8 @@ import android.system.keystore2.IKeystoreSecurityLevel;
|
||||
import android.system.keystore2.KeyDescriptor;
|
||||
import android.system.keystore2.KeyMetadata;
|
||||
import android.system.keystore2.ResponseCode;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import libcore.util.EmptyArray;
|
||||
@@ -478,7 +484,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
}
|
||||
throw p;
|
||||
}
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
} catch (UnrecoverableKeyException | IllegalArgumentException
|
||||
| DeviceIdAttestationException e) {
|
||||
throw new ProviderException(
|
||||
"Failed to construct key object from newly generated key pair.", e);
|
||||
} finally {
|
||||
@@ -496,7 +503,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
}
|
||||
|
||||
private void addAttestationParameters(@NonNull List<KeyParameter> params)
|
||||
throws ProviderException {
|
||||
throws ProviderException, IllegalArgumentException, DeviceIdAttestationException {
|
||||
byte[] challenge = mSpec.getAttestationChallenge();
|
||||
|
||||
if (challenge != null) {
|
||||
@@ -526,15 +533,69 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
Build.MODEL.getBytes(StandardCharsets.UTF_8)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if (mSpec.isDevicePropertiesAttestationIncluded()) {
|
||||
throw new ProviderException("An attestation challenge must be provided when "
|
||||
+ "requesting device properties attestation.");
|
||||
|
||||
int[] idTypes = mSpec.getAttestationIds();
|
||||
if (idTypes == null) {
|
||||
return;
|
||||
}
|
||||
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:
|
||||
params.add(KeyStore2ParameterUtils.makeBytes(
|
||||
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");
|
||||
}
|
||||
params.add(KeyStore2ParameterUtils.makeBytes(
|
||||
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");
|
||||
}
|
||||
params.add(KeyStore2ParameterUtils.makeBytes(
|
||||
KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID,
|
||||
meid.getBytes(StandardCharsets.UTF_8)
|
||||
));
|
||||
break;
|
||||
}
|
||||
case AttestationUtils.USE_INDIVIDUAL_ATTESTATION: {
|
||||
params.add(KeyStore2ParameterUtils.makeBool(
|
||||
KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown device ID type " + idType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<KeyParameter> constructKeyGenerationArguments() {
|
||||
private Collection<KeyParameter> constructKeyGenerationArguments()
|
||||
throws DeviceIdAttestationException, IllegalArgumentException {
|
||||
List<KeyParameter> params = new ArrayList<>();
|
||||
params.add(KeyStore2ParameterUtils.makeInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits));
|
||||
params.add(KeyStore2ParameterUtils.makeEnum(
|
||||
|
||||
Reference in New Issue
Block a user