blob: 928d128d6d16dc387a827307dafdb1cc7a8f87f6 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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.android.server.security;
import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.framework.protobuf.ByteString;
import com.android.internal.org.bouncycastle.asn1.ASN1Boolean;
import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
import com.android.internal.org.bouncycastle.asn1.ASN1Enumerated;
import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
import com.android.internal.org.bouncycastle.asn1.ASN1Set;
import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Parsed {@link X509Certificate} attestation extension values for Android Keystore attestations.
*
* Pull fields out of the top-level sequence. A full description of this structure is at
* https://source.android.com/security/keystore/attestation.
*
* If a value is null or empty, then it was not set/found in the extension values.
*
*/
class AndroidKeystoreAttestationVerificationAttributes {
// The OID for the extension Android Keymaster puts into device-generated certificates.
private static final String ANDROID_KEYMASTER_KEY_DESCRIPTION_EXTENSION_OID =
"1.3.6.1.4.1.11129.2.1.17";
// ASN.1 sequence index values for the Android Keymaster extension.
private static final int ATTESTATION_VERSION_INDEX = 0;
private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
private static final int KEYMASTER_VERSION_INDEX = 2;
private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
private static final int ATTESTATION_CHALLENGE_INDEX = 4;
private static final int KEYMASTER_UNIQUE_ID_INDEX = 5;
private static final int SW_ENFORCED_INDEX = 6;
private static final int HW_ENFORCED_INDEX = 7;
private static final int VERIFIED_BOOT_KEY_INDEX = 0;
private static final int VERIFIED_BOOT_LOCKED_INDEX = 1;
private static final int VERIFIED_BOOT_STATE_INDEX = 2;
private static final int VERIFIED_BOOT_HASH_INDEX = 3;
// ASN.1 sequence index values for the Android Keystore application id.
private static final int PACKAGE_INFO_SET_INDEX = 0;
private static final int PACKAGE_SIGNATURE_SET_INDEX = 1;
private static final int PACKAGE_INFO_NAME_INDEX = 0;
private static final int PACKAGE_INFO_VERSION_INDEX = 1;
// See these AOSP files: hardware/libhardware/include/hardware/hw_auth_token.h
private static final int HW_AUTH_NONE = 0;
// Some keymaster constants. See this AOSP file:
// hardware/libhardware/include/hardware/keymaster_defs.h
private static final int KM_TAG_NO_AUTH_REQUIRED = 503;
private static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = 509;
private static final int KM_TAG_ALL_APPLICATIONS = 600;
private static final int KM_TAG_ROOT_OF_TRUST = 704;
private static final int KM_TAG_OS_VERSION = 705;
private static final int KM_TAG_OS_PATCHLEVEL = 706;
private static final int KM_TAG_ATTESTATION_APPLICATION_ID = 709;
private static final int KM_TAG_ATTESTATION_ID_BRAND = 710;
private static final int KM_TAG_ATTESTATION_ID_DEVICE = 711;
private static final int KM_TAG_ATTESTATION_ID_PRODUCT = 712;
private static final int KM_TAG_VENDOR_PATCHLEVEL = 718;
private static final int KM_TAG_BOOT_PATCHLEVEL = 719;
private static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
private static final int KM_VERIFIED_BOOT_STATE_VERIFIED = 0;
private static final int KM_VERIFIED_BOOT_STATE_SELF_SIGNED = 1;
private static final int KM_VERIFIED_BOOT_STATE_UNVERIFIED = 2;
private static final int KM_VERIFIED_BOOT_STATE_FAILED = 3;
private Integer mAttestationVersion = null;
private SecurityLevel mAttestationSecurityLevel = null;
private boolean mAttestationHardwareBacked = false;
private Integer mKeymasterVersion = null;
private SecurityLevel mKeymasterSecurityLevel = null;
private boolean mKeymasterHardwareBacked = false;
private ByteString mAttestationChallenge = null;
private ByteString mKeymasterUniqueId = null;
private String mDeviceBrand = null;
private String mDeviceName = null;
private String mDeviceProductName = null;
private boolean mKeyAllowedForAllApplications = false;
private Integer mKeyAuthenticatorType = null;
private Integer mKeyBootPatchLevel = null;
private Integer mKeyOsPatchLevel = null;
private Integer mKeyOsVersion = null;
private Integer mKeyVendorPatchLevel = null;
private Boolean mKeyRequiresUnlockedDevice = null;
private ByteString mVerifiedBootHash = null;
private ByteString mVerifiedBootKey = null;
private Boolean mVerifiedBootLocked = null;
private VerifiedBootState mVerifiedBootState = null;
private Map<String, Long> mApplicationPackageNameVersion = null;
private List<ByteString> mApplicationCertificateDigests = null;
enum VerifiedBootState {
VERIFIED,
SELF_SIGNED,
UNVERIFIED,
FAILED
}
enum SecurityLevel {
SOFTWARE,
TRUSTED_ENVIRONMENT,
STRONG_BOX
}
/**
* Extracts attestation extension properties from {@link X509Certificate}
* and returns a {@link AndroidKeystoreAttestationVerificationAttributes} that encapsulates the
* properties.
*/
@NonNull
static AndroidKeystoreAttestationVerificationAttributes fromCertificate(
@NonNull X509Certificate x509Certificate)
throws CertificateEncodingException, IOException {
return new AndroidKeystoreAttestationVerificationAttributes(x509Certificate);
}
int getAttestationVersion() {
return mAttestationVersion;
}
@Nullable
SecurityLevel getAttestationSecurityLevel() {
return mAttestationSecurityLevel;
}
boolean isAttestationHardwareBacked() {
return mAttestationHardwareBacked;
}
int getKeymasterVersion() {
return mKeymasterVersion;
}
@Nullable
SecurityLevel getKeymasterSecurityLevel() {
return mKeymasterSecurityLevel;
}
boolean isKeymasterHardwareBacked() {
return mKeymasterHardwareBacked;
}
@Nullable
ByteString getAttestationChallenge() {
return mAttestationChallenge;
}
@Nullable
ByteString getKeymasterUniqueId() {
return mKeymasterUniqueId;
}
@Nullable
String getDeviceBrand() {
return mDeviceBrand;
}
@Nullable
String getDeviceName() {
return mDeviceName;
}
@Nullable
String getDeviceProductName() {
return mDeviceProductName;
}
boolean isKeyAllowedForAllApplications() {
return mKeyAllowedForAllApplications;
}
int getKeyAuthenticatorType() {
if (mKeyAuthenticatorType == null) {
throw new IllegalStateException("KeyAuthenticatorType is not set.");
}
return mKeyAuthenticatorType;
}
int getKeyBootPatchLevel() {
if (mKeyBootPatchLevel == null) {
throw new IllegalStateException("KeyBootPatchLevel is not set.");
}
return mKeyBootPatchLevel;
}
int getKeyOsPatchLevel() {
if (mKeyOsPatchLevel == null) {
throw new IllegalStateException("KeyOsPatchLevel is not set.");
}
return mKeyOsPatchLevel;
}
int getKeyVendorPatchLevel() {
if (mKeyVendorPatchLevel == null) {
throw new IllegalStateException("KeyVendorPatchLevel is not set.");
}
return mKeyVendorPatchLevel;
}
int getKeyOsVersion() {
if (mKeyOsVersion == null) {
throw new IllegalStateException("KeyOsVersion is not set.");
}
return mKeyOsVersion;
}
boolean isKeyRequiresUnlockedDevice() {
if (mKeyRequiresUnlockedDevice == null) {
throw new IllegalStateException("KeyRequiresUnlockedDevice is not set.");
}
return mKeyRequiresUnlockedDevice;
}
@Nullable
ByteString getVerifiedBootHash() {
return mVerifiedBootHash;
}
@Nullable
ByteString getVerifiedBootKey() {
return mVerifiedBootKey;
}
boolean isVerifiedBootLocked() {
if (mVerifiedBootLocked == null) {
throw new IllegalStateException("VerifiedBootLocked is not set.");
}
return mVerifiedBootLocked;
}
@Nullable
VerifiedBootState getVerifiedBootState() {
return mVerifiedBootState;
}
@Nullable
Map<String, Long> getApplicationPackageNameVersion() {
return Collections.unmodifiableMap(mApplicationPackageNameVersion);
}
@Nullable
List<ByteString> getApplicationCertificateDigests() {
return Collections.unmodifiableList(mApplicationCertificateDigests);
}
private AndroidKeystoreAttestationVerificationAttributes(X509Certificate x509Certificate)
throws CertificateEncodingException, IOException {
Certificate certificate = Certificate.getInstance(
new ASN1InputStream(x509Certificate.getEncoded()).readObject());
ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions()
.getExtensionParsedValue(
new ASN1ObjectIdentifier(ANDROID_KEYMASTER_KEY_DESCRIPTION_EXTENSION_OID));
if (keyAttributes == null) {
throw new CertificateEncodingException(
"No attestation extension found in certificate.");
}
this.mAttestationVersion = getIntegerFromAsn1(
keyAttributes.getObjectAt(ATTESTATION_VERSION_INDEX));
this.mAttestationSecurityLevel = getSecurityLevelEnum(
keyAttributes.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
this.mAttestationHardwareBacked =
this.mAttestationSecurityLevel == SecurityLevel.TRUSTED_ENVIRONMENT;
this.mAttestationChallenge = getOctetsFromAsn1(
keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX));
this.mKeymasterVersion = getIntegerFromAsn1(
keyAttributes.getObjectAt(KEYMASTER_VERSION_INDEX));
this.mKeymasterUniqueId = getOctetsFromAsn1(
keyAttributes.getObjectAt(KEYMASTER_UNIQUE_ID_INDEX));
this.mKeymasterSecurityLevel = getSecurityLevelEnum(
keyAttributes.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
this.mKeymasterHardwareBacked =
this.mKeymasterSecurityLevel == SecurityLevel.TRUSTED_ENVIRONMENT;
ASN1Encodable[] softwareEnforced = ((ASN1Sequence)
keyAttributes.getObjectAt(SW_ENFORCED_INDEX)).toArray();
for (ASN1Encodable entry : softwareEnforced) {
ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
switch (taggedEntry.getTagNo()) {
case KM_TAG_ATTESTATION_APPLICATION_ID:
parseAttestationApplicationId(
getOctetsFromAsn1(taggedEntry.getObject()).toByteArray());
break;
case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
this.mKeyRequiresUnlockedDevice = getBoolFromAsn1(taggedEntry.getObject());
break;
default:
break;
}
}
ASN1Encodable[] hardwareEnforced = ((ASN1Sequence)
keyAttributes.getObjectAt(HW_ENFORCED_INDEX)).toArray();
for (ASN1Encodable entry : hardwareEnforced) {
ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
switch (taggedEntry.getTagNo()) {
case KM_TAG_NO_AUTH_REQUIRED:
this.mKeyAuthenticatorType = HW_AUTH_NONE;
break;
case KM_TAG_ALL_APPLICATIONS:
this.mKeyAllowedForAllApplications = true;
break;
case KM_TAG_ROOT_OF_TRUST:
ASN1Sequence rootOfTrust = (ASN1Sequence) taggedEntry.getObject();
this.mVerifiedBootKey =
getOctetsFromAsn1(rootOfTrust.getObjectAt(VERIFIED_BOOT_KEY_INDEX));
this.mVerifiedBootLocked =
getBoolFromAsn1(rootOfTrust.getObjectAt(VERIFIED_BOOT_LOCKED_INDEX));
this.mVerifiedBootState =
getVerifiedBootStateEnum(
rootOfTrust.getObjectAt(VERIFIED_BOOT_STATE_INDEX));
// The verified boot hash was added in structure version 3 (Keymaster 4.0).
if (mAttestationVersion >= 3) {
this.mVerifiedBootHash =
getOctetsFromAsn1(
rootOfTrust.getObjectAt(VERIFIED_BOOT_HASH_INDEX));
}
break;
case KM_TAG_OS_VERSION:
this.mKeyOsVersion = getIntegerFromAsn1(taggedEntry.getObject());
break;
case KM_TAG_OS_PATCHLEVEL:
this.mKeyOsPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
break;
case KM_TAG_ATTESTATION_ID_BRAND:
this.mDeviceBrand = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
break;
case KM_TAG_ATTESTATION_ID_DEVICE:
this.mDeviceName = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
break;
case KM_TAG_ATTESTATION_ID_PRODUCT:
this.mDeviceProductName = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
break;
case KM_TAG_VENDOR_PATCHLEVEL:
this.mKeyVendorPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
break;
case KM_TAG_BOOT_PATCHLEVEL:
this.mKeyBootPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
break;
default:
break;
}
}
}
private void parseAttestationApplicationId(byte [] attestationApplicationId)
throws IOException {
ASN1Sequence outerSequence = ASN1Sequence.getInstance(
new ASN1InputStream(attestationApplicationId).readObject());
Map<String, Long> packageNameVersion = new HashMap<>();
ASN1Set packageInfoSet = (ASN1Set) outerSequence.getObjectAt(PACKAGE_INFO_SET_INDEX);
for (ASN1Encodable packageInfoEntry : packageInfoSet.toArray()) {
ASN1Sequence packageInfoSequence = (ASN1Sequence) packageInfoEntry;
packageNameVersion.put(
getUtf8FromOctetsFromAsn1(
packageInfoSequence.getObjectAt(PACKAGE_INFO_NAME_INDEX)),
getLongFromAsn1(packageInfoSequence.getObjectAt(PACKAGE_INFO_VERSION_INDEX)));
}
List<ByteString> certificateDigests = new ArrayList<>();
ASN1Set certificateDigestSet =
(ASN1Set) outerSequence.getObjectAt(PACKAGE_SIGNATURE_SET_INDEX);
for (ASN1Encodable certificateDigestEntry : certificateDigestSet.toArray()) {
certificateDigests.add(getOctetsFromAsn1(certificateDigestEntry));
}
this.mApplicationPackageNameVersion = Collections.unmodifiableMap(packageNameVersion);
this.mApplicationCertificateDigests = Collections.unmodifiableList(certificateDigests);
}
private VerifiedBootState getVerifiedBootStateEnum(ASN1Encodable asn1) {
int verifiedBoot = getEnumFromAsn1(asn1);
switch (verifiedBoot) {
case KM_VERIFIED_BOOT_STATE_VERIFIED:
return VerifiedBootState.VERIFIED;
case KM_VERIFIED_BOOT_STATE_SELF_SIGNED:
return VerifiedBootState.SELF_SIGNED;
case KM_VERIFIED_BOOT_STATE_UNVERIFIED:
return VerifiedBootState.UNVERIFIED;
case KM_VERIFIED_BOOT_STATE_FAILED:
return VerifiedBootState.FAILED;
default:
throw new IllegalArgumentException("Invalid verified boot state.");
}
}
private SecurityLevel getSecurityLevelEnum(ASN1Encodable asn1) {
int securityLevel = getEnumFromAsn1(asn1);
switch (securityLevel) {
case KM_SECURITY_LEVEL_SOFTWARE:
return SecurityLevel.SOFTWARE;
case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
return SecurityLevel.TRUSTED_ENVIRONMENT;
case KM_SECURITY_LEVEL_STRONG_BOX:
return SecurityLevel.STRONG_BOX;
default:
throw new IllegalArgumentException("Invalid security level.");
}
}
@NonNull
private ByteString getOctetsFromAsn1(ASN1Encodable asn1) {
return ByteString.copyFrom(((ASN1OctetString) asn1).getOctets());
}
@NonNull
private String getUtf8FromOctetsFromAsn1(ASN1Encodable asn1) {
return new String(((ASN1OctetString) asn1).getOctets(), StandardCharsets.UTF_8);
}
@NonNull
private int getIntegerFromAsn1(ASN1Encodable asn1) {
return ((ASN1Integer) asn1).getValue().intValueExact();
}
@NonNull
private long getLongFromAsn1(ASN1Encodable asn1) {
return ((ASN1Integer) asn1).getValue().longValueExact();
}
@NonNull
private int getEnumFromAsn1(ASN1Encodable asn1) {
return ((ASN1Enumerated) asn1).getValue().intValueExact();
}
@Nullable
private Boolean getBoolFromAsn1(ASN1Encodable asn1) {
if (asn1 instanceof ASN1Boolean) {
return ((ASN1Boolean) asn1).isTrue();
}
return null;
}
}