blob: 81c35ece3fbc798bff85d9633b455fdcfcb43226 [file] [log] [blame]
/* Copyright 2019, The Android Open Source Project, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.attestation;
import static com.google.android.attestation.AuthorizationList.UserAuthType.FINGERPRINT;
import static com.google.android.attestation.AuthorizationList.UserAuthType.PASSWORD;
import static com.google.android.attestation.AuthorizationList.UserAuthType.USER_AUTH_TYPE_ANY;
import static com.google.android.attestation.AuthorizationList.UserAuthType.USER_AUTH_TYPE_NONE;
import static com.google.android.attestation.Constants.KM_TAG_ACTIVE_DATE_TIME;
import static com.google.android.attestation.Constants.KM_TAG_ALGORITHM;
import static com.google.android.attestation.Constants.KM_TAG_ALLOW_WHILE_ON_BODY;
import static com.google.android.attestation.Constants.KM_TAG_ALL_APPLICATIONS;
import static com.google.android.attestation.Constants.KM_TAG_APPLICATION_ID;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_APPLICATION_ID;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_BRAND;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_DEVICE;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_IMEI;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_MANUFACTURER;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_MEID;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_MODEL;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_PRODUCT;
import static com.google.android.attestation.Constants.KM_TAG_ATTESTATION_ID_SERIAL;
import static com.google.android.attestation.Constants.KM_TAG_AUTH_TIMEOUT;
import static com.google.android.attestation.Constants.KM_TAG_BOOT_PATCH_LEVEL;
import static com.google.android.attestation.Constants.KM_TAG_CREATION_DATE_TIME;
import static com.google.android.attestation.Constants.KM_TAG_DIGEST;
import static com.google.android.attestation.Constants.KM_TAG_EC_CURVE;
import static com.google.android.attestation.Constants.KM_TAG_KEY_SIZE;
import static com.google.android.attestation.Constants.KM_TAG_NO_AUTH_REQUIRED;
import static com.google.android.attestation.Constants.KM_TAG_ORIGIN;
import static com.google.android.attestation.Constants.KM_TAG_ORIGINATION_EXPIRE_DATE_TIME;
import static com.google.android.attestation.Constants.KM_TAG_OS_PATCH_LEVEL;
import static com.google.android.attestation.Constants.KM_TAG_OS_VERSION;
import static com.google.android.attestation.Constants.KM_TAG_PADDING;
import static com.google.android.attestation.Constants.KM_TAG_PURPOSE;
import static com.google.android.attestation.Constants.KM_TAG_ROLLBACK_RESISTANCE;
import static com.google.android.attestation.Constants.KM_TAG_ROLLBACK_RESISTANT;
import static com.google.android.attestation.Constants.KM_TAG_ROOT_OF_TRUST;
import static com.google.android.attestation.Constants.KM_TAG_RSA_PUBLIC_EXPONENT;
import static com.google.android.attestation.Constants.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED;
import static com.google.android.attestation.Constants.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED;
import static com.google.android.attestation.Constants.KM_TAG_UNLOCKED_DEVICE_REQUIRED;
import static com.google.android.attestation.Constants.KM_TAG_USAGE_EXPIRE_DATE_TIME;
import static com.google.android.attestation.Constants.KM_TAG_USER_AUTH_TYPE;
import static com.google.android.attestation.Constants.KM_TAG_VENDOR_PATCH_LEVEL;
import static com.google.android.attestation.Constants.UINT32_MAX;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DEROctetString;
/**
* This data structure contains the key pair's properties themselves, as defined in the Keymaster
* hardware abstraction layer (HAL). You compare these values to the device's current state or to a
* set of expected values to verify that a key pair is still valid for use in your app.
*/
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class AuthorizationList {
/** Specifies the types of user authenticators that may be used to authorize this key. */
public enum UserAuthType {
USER_AUTH_TYPE_NONE,
PASSWORD,
FINGERPRINT,
USER_AUTH_TYPE_ANY
}
public final Optional<Set<Integer>> purpose;
public final Optional<Integer> algorithm;
public final Optional<Integer> keySize;
public final Optional<Set<Integer>> digest;
public final Optional<Set<Integer>> padding;
public final Optional<Integer> ecCurve;
public final Optional<Long> rsaPublicExponent;
public final boolean rollbackResistance;
public final Optional<Instant> activeDateTime;
public final Optional<Instant> originationExpireDateTime;
public final Optional<Instant> usageExpireDateTime;
public final boolean noAuthRequired;
public final Optional<UserAuthType> userAuthType;
public final Optional<Duration> authTimeout;
public final boolean allowWhileOnBody;
public final boolean trustedUserPresenceRequired;
public final boolean trustedConfirmationRequired;
public final boolean unlockedDeviceRequired;
public final boolean allApplications;
public final Optional<byte[]> applicationId;
public final Optional<Instant> creationDateTime;
public final Optional<Integer> origin;
public final boolean rollbackResistant;
public final Optional<RootOfTrust> rootOfTrust;
public final Optional<Integer> osVersion;
public final Optional<Integer> osPatchLevel;
public final Optional<AttestationApplicationId> attestationApplicationId;
public final Optional<byte[]> attestationApplicationIdBytes;
public final Optional<byte[]> attestationIdBrand;
public final Optional<byte[]> attestationIdDevice;
public final Optional<byte[]> attestationIdProduct;
public final Optional<byte[]> attestationIdSerial;
public final Optional<byte[]> attestationIdImei;
public final Optional<byte[]> attestationIdMeid;
public final Optional<byte[]> attestationIdManufacturer;
public final Optional<byte[]> attestationIdModel;
public final Optional<Integer> vendorPatchLevel;
public final Optional<Integer> bootPatchLevel;
private AuthorizationList(ASN1Encodable[] authorizationList, int attestationVersion) {
Map<Integer, ASN1Primitive> authorizationMap = getAuthorizationMap(authorizationList);
this.purpose = findOptionalIntegerSetAuthorizationListEntry(authorizationMap, KM_TAG_PURPOSE);
this.algorithm = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_ALGORITHM);
this.keySize = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_KEY_SIZE);
this.digest = findOptionalIntegerSetAuthorizationListEntry(authorizationMap, KM_TAG_DIGEST);
this.padding = findOptionalIntegerSetAuthorizationListEntry(authorizationMap, KM_TAG_PADDING);
this.ecCurve = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_EC_CURVE);
this.rsaPublicExponent =
findOptionalLongAuthorizationListEntry(authorizationMap, KM_TAG_RSA_PUBLIC_EXPONENT);
this.rollbackResistance =
findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_ROLLBACK_RESISTANCE);
this.activeDateTime =
findOptionalInstantMillisAuthorizationListEntry(authorizationMap, KM_TAG_ACTIVE_DATE_TIME);
this.originationExpireDateTime =
findOptionalInstantMillisAuthorizationListEntry(
authorizationMap, KM_TAG_ORIGINATION_EXPIRE_DATE_TIME);
this.usageExpireDateTime =
findOptionalInstantMillisAuthorizationListEntry(
authorizationMap, KM_TAG_USAGE_EXPIRE_DATE_TIME);
this.noAuthRequired =
findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_NO_AUTH_REQUIRED);
this.userAuthType = findOptionalUserAuthType(authorizationMap, KM_TAG_USER_AUTH_TYPE);
this.authTimeout =
findOptionalDurationSecondsAuthorizationListEntry(authorizationMap, KM_TAG_AUTH_TIMEOUT);
this.allowWhileOnBody =
findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_ALLOW_WHILE_ON_BODY);
this.trustedUserPresenceRequired =
findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
this.trustedConfirmationRequired =
findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
this.unlockedDeviceRequired =
findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_UNLOCKED_DEVICE_REQUIRED);
this.allApplications =
findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_ALL_APPLICATIONS);
this.applicationId =
findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_APPLICATION_ID);
this.creationDateTime =
findOptionalInstantMillisAuthorizationListEntry(
authorizationMap, KM_TAG_CREATION_DATE_TIME);
this.origin = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_ORIGIN);
this.rollbackResistant =
findBooleanAuthorizationListEntry(authorizationMap, KM_TAG_ROLLBACK_RESISTANT);
this.rootOfTrust =
Optional.ofNullable(
RootOfTrust.createRootOfTrust(
(ASN1Sequence) findAuthorizationListEntry(authorizationMap, KM_TAG_ROOT_OF_TRUST),
attestationVersion));
this.osVersion = findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_OS_VERSION);
this.osPatchLevel =
findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_OS_PATCH_LEVEL);
this.attestationApplicationId =
Optional.ofNullable(
AttestationApplicationId.createAttestationApplicationId(
(DEROctetString)
findAuthorizationListEntry(
authorizationMap, KM_TAG_ATTESTATION_APPLICATION_ID)));
this.attestationApplicationIdBytes =
findOptionalByteArrayAuthorizationListEntry(
authorizationMap, KM_TAG_ATTESTATION_APPLICATION_ID);
this.attestationIdBrand =
findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_BRAND);
this.attestationIdDevice =
findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_DEVICE);
this.attestationIdProduct =
findOptionalByteArrayAuthorizationListEntry(
authorizationMap, KM_TAG_ATTESTATION_ID_PRODUCT);
this.attestationIdSerial =
findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_SERIAL);
this.attestationIdImei =
findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_IMEI);
this.attestationIdMeid =
findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_MEID);
this.attestationIdManufacturer =
findOptionalByteArrayAuthorizationListEntry(
authorizationMap, KM_TAG_ATTESTATION_ID_MANUFACTURER);
this.attestationIdModel =
findOptionalByteArrayAuthorizationListEntry(authorizationMap, KM_TAG_ATTESTATION_ID_MODEL);
this.vendorPatchLevel =
findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_VENDOR_PATCH_LEVEL);
this.bootPatchLevel =
findOptionalIntegerAuthorizationListEntry(authorizationMap, KM_TAG_BOOT_PATCH_LEVEL);
}
static AuthorizationList createAuthorizationList(
ASN1Encodable[] authorizationList, int attestationVersion) {
return new AuthorizationList(authorizationList, attestationVersion);
}
private static Map<Integer, ASN1Primitive> getAuthorizationMap(
ASN1Encodable[] authorizationList) {
Map<Integer, ASN1Primitive> authorizationMap = new HashMap<>();
for (ASN1Encodable entry : authorizationList) {
ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
authorizationMap.put(taggedEntry.getTagNo(), taggedEntry.getObject());
}
return authorizationMap;
}
private static ASN1Primitive findAuthorizationListEntry(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
return authorizationMap.getOrDefault(tag, null);
}
private static Optional<Set<Integer>> findOptionalIntegerSetAuthorizationListEntry(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
ASN1Set asn1Set = (ASN1Set) findAuthorizationListEntry(authorizationMap, tag);
if (asn1Set == null) {
return Optional.empty();
}
Set<Integer> entrySet = new HashSet<>();
for (ASN1Encodable value : asn1Set) {
entrySet.add(ASN1Parsing.getIntegerFromAsn1(value));
}
return Optional.of(entrySet);
}
private static Optional<Duration> findOptionalDurationSecondsAuthorizationListEntry(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
Optional<Integer> seconds = findOptionalIntegerAuthorizationListEntry(authorizationMap, tag);
return seconds.map(Duration::ofSeconds);
}
private static Optional<Integer> findOptionalIntegerAuthorizationListEntry(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
ASN1Primitive entry = findAuthorizationListEntry(authorizationMap, tag);
return Optional.ofNullable(entry).map(ASN1Parsing::getIntegerFromAsn1);
}
private static Optional<Instant> findOptionalInstantMillisAuthorizationListEntry(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
Optional<Long> millis = findOptionalLongAuthorizationListEntry(authorizationMap, tag);
return millis.map(Instant::ofEpochMilli);
}
private static Optional<Long> findOptionalLongAuthorizationListEntry(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
ASN1Integer longEntry = ((ASN1Integer) findAuthorizationListEntry(authorizationMap, tag));
return Optional.ofNullable(longEntry).map(value -> value.getValue().longValue());
}
private static boolean findBooleanAuthorizationListEntry(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
return null != findAuthorizationListEntry(authorizationMap, tag);
}
private static Optional<byte[]> findOptionalByteArrayAuthorizationListEntry(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(authorizationMap, tag);
return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
}
private static Optional<UserAuthType> findOptionalUserAuthType(
Map<Integer, ASN1Primitive> authorizationMap, int tag) {
Optional<Long> userAuthType = findOptionalLongAuthorizationListEntry(authorizationMap, tag);
return userAuthType.map(AuthorizationList::userAuthTypeToEnum);
}
// Visible for testing.
static UserAuthType userAuthTypeToEnum(long userAuthType) {
if (userAuthType == 0L) {
return USER_AUTH_TYPE_NONE;
} else if (userAuthType == 1L) {
return PASSWORD;
} else if (userAuthType == 2L) {
return FINGERPRINT;
} else if (userAuthType == UINT32_MAX) {
return USER_AUTH_TYPE_ANY;
}
throw new IllegalArgumentException("Invalid User Auth Type.");
}
}