1 /* 2 * Copyright 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.security.identity.cts; 18 19 import java.io.IOException; 20 import java.security.cert.X509Certificate; 21 import java.util.Map; 22 import java.util.HashMap; 23 import java.util.Set; 24 import java.util.Optional; 25 26 import org.bouncycastle.asn1.ASN1InputStream; 27 import org.bouncycastle.asn1.ASN1OctetString; 28 import org.bouncycastle.asn1.ASN1Sequence; 29 import org.bouncycastle.asn1.ASN1Boolean; 30 import org.bouncycastle.asn1.ASN1Encodable; 31 import org.bouncycastle.asn1.ASN1Enumerated; 32 import org.bouncycastle.asn1.ASN1Integer; 33 import org.bouncycastle.asn1.ASN1OctetString; 34 import org.bouncycastle.asn1.ASN1Primitive; 35 import org.bouncycastle.asn1.ASN1Set; 36 import org.bouncycastle.asn1.ASN1TaggedObject; 37 import org.bouncycastle.asn1.DEROctetString; 38 39 // This code is loosely based on https://github.com/google/android-key-attestation 40 41 class ParsedAttestationRecord { 42 private static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17"; 43 44 enum SecurityLevel { 45 SOFTWARE, 46 TRUSTED_ENVIRONMENT, 47 STRONG_BOX 48 } 49 50 private static final int ATTESTATION_VERSION_INDEX = 0; 51 private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1; 52 private static final int KEYMASTER_VERSION_INDEX = 2; 53 private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3; 54 private static final int ATTESTATION_CHALLENGE_INDEX = 4; 55 private static final int UNIQUE_ID_INDEX = 5; 56 private static final int SW_ENFORCED_INDEX = 6; 57 private static final int TEE_ENFORCED_INDEX = 7; 58 59 // Some security values. The complete list is in this AOSP file: 60 // hardware/libhardware/include/hardware/keymaster_defs.h 61 private static final int KM_SECURITY_LEVEL_SOFTWARE = 0; 62 private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1; 63 private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2; 64 65 private int attestationVersion; 66 private SecurityLevel attestationSecurityLevel; 67 private int keymasterVersion; 68 private SecurityLevel keymasterSecurityLevel; 69 private byte[] attestationChallenge; 70 private byte[] uniqueId; 71 72 private Map<Integer, ASN1Primitive> softwareEnforcedAuthorizations; 73 private Map<Integer, ASN1Primitive> teeEnforcedAuthorizations; 74 getAttestationVersion()75 public int getAttestationVersion() { 76 return attestationVersion; 77 } 78 getAttestationSecurityLevel()79 public SecurityLevel getAttestationSecurityLevel() { 80 return attestationSecurityLevel; 81 } 82 getKeymasterVersion()83 public int getKeymasterVersion() { 84 return keymasterVersion; 85 } 86 getKeymasterSecurityLevel()87 public SecurityLevel getKeymasterSecurityLevel() { 88 return attestationSecurityLevel; 89 } 90 getAttestationChallenge()91 public byte[] getAttestationChallenge() { 92 return attestationChallenge; 93 } 94 getUniqueId()95 public byte[] getUniqueId() { 96 return uniqueId; 97 } 98 getSoftwareEnforcedAuthorizationTags()99 public Set<Integer> getSoftwareEnforcedAuthorizationTags() { 100 return softwareEnforcedAuthorizations.keySet(); 101 } 102 getTeeEnforcedAuthorizationTags()103 public Set<Integer> getTeeEnforcedAuthorizationTags() { 104 return teeEnforcedAuthorizations.keySet(); 105 } 106 findAuthorizationListEntry( Map<Integer, ASN1Primitive> authorizationMap, int tag)107 private static ASN1Primitive findAuthorizationListEntry( 108 Map<Integer, ASN1Primitive> authorizationMap, int tag) { 109 return authorizationMap.getOrDefault(tag, null); 110 } 111 getSoftwareAuthorizationInteger(int tag)112 public Optional<Integer> getSoftwareAuthorizationInteger(int tag) { 113 ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag); 114 return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1); 115 } 116 getTeeAuthorizationInteger(int tag)117 public Optional<Integer> getTeeAuthorizationInteger(int tag) { 118 ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag); 119 return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1); 120 } getSoftwareAuthorizationBoolean(int tag)121 public boolean getSoftwareAuthorizationBoolean(int tag) { 122 ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag); 123 return entry != null; 124 } 125 getTeeAuthorizationBoolean(int tag)126 public boolean getTeeAuthorizationBoolean(int tag) { 127 ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag); 128 return entry != null; 129 } 130 getSoftwareAuthorizationByteString(int tag)131 public Optional<byte[]> getSoftwareAuthorizationByteString(int tag) { 132 ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(softwareEnforcedAuthorizations, tag); 133 return Optional.ofNullable(entry).map(ASN1OctetString::getOctets); 134 } 135 getTeeAuthorizationByteString(int tag)136 public Optional<byte[]> getTeeAuthorizationByteString(int tag) { 137 ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(teeEnforcedAuthorizations, tag); 138 return Optional.ofNullable(entry).map(ASN1OctetString::getOctets); 139 } 140 getBooleanFromAsn1(ASN1Encodable asn1Value)141 private static boolean getBooleanFromAsn1(ASN1Encodable asn1Value) { 142 if (asn1Value instanceof ASN1Boolean) { 143 return ((ASN1Boolean) asn1Value).isTrue(); 144 } else { 145 throw new RuntimeException( 146 "Boolean value expected; found " + asn1Value.getClass().getName() + " instead."); 147 } 148 } 149 getIntegerFromAsn1(ASN1Encodable asn1Value)150 private static int getIntegerFromAsn1(ASN1Encodable asn1Value) { 151 if (asn1Value instanceof ASN1Integer) { 152 return ((ASN1Integer) asn1Value).getValue().intValue(); 153 } else if (asn1Value instanceof ASN1Enumerated) { 154 return ((ASN1Enumerated) asn1Value).getValue().intValue(); 155 } else { 156 throw new IllegalArgumentException( 157 "Integer value expected; found " + asn1Value.getClass().getName() + " instead."); 158 } 159 } 160 getAuthorizationMap( ASN1Encodable[] authorizationList)161 private static Map<Integer, ASN1Primitive> getAuthorizationMap( 162 ASN1Encodable[] authorizationList) { 163 Map<Integer, ASN1Primitive> authorizationMap = new HashMap<>(); 164 for (ASN1Encodable entry : authorizationList) { 165 ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry; 166 authorizationMap.put(taggedEntry.getTagNo(), taggedEntry.getObject()); 167 } 168 return authorizationMap; 169 } 170 ParsedAttestationRecord(X509Certificate cert)171 public ParsedAttestationRecord(X509Certificate cert) throws IOException { 172 byte[] attestationExtensionBytes = cert.getExtensionValue(KEY_DESCRIPTION_OID); 173 if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) { 174 throw new IllegalArgumentException("Couldn't find keystore attestation extension."); 175 } 176 177 ASN1Sequence seq; 178 try (ASN1InputStream asn1InputStream = new ASN1InputStream(attestationExtensionBytes)) { 179 // The extension contains one object, a sequence, in the 180 // Distinguished Encoding Rules (DER)-encoded form. Get the DER 181 // bytes. 182 byte[] derSequenceBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets(); 183 // Decode the bytes as an ASN1 sequence object. 184 try (ASN1InputStream seqInputStream = new ASN1InputStream(derSequenceBytes)) { 185 seq = (ASN1Sequence) seqInputStream.readObject(); 186 } 187 } 188 189 this.attestationVersion = getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX)); 190 this.attestationSecurityLevel = 191 securityLevelToEnum(getIntegerFromAsn1( 192 seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX))); 193 this.keymasterVersion = getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX)); 194 this.keymasterSecurityLevel = securityLevelToEnum( 195 getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX))); 196 this.attestationChallenge = 197 ((ASN1OctetString) seq.getObjectAt(ATTESTATION_CHALLENGE_INDEX)).getOctets(); 198 this.uniqueId = ((ASN1OctetString) seq.getObjectAt(UNIQUE_ID_INDEX)).getOctets(); 199 200 this.softwareEnforcedAuthorizations = getAuthorizationMap( 201 ((ASN1Sequence) seq.getObjectAt(SW_ENFORCED_INDEX)).toArray()); 202 203 this.teeEnforcedAuthorizations = getAuthorizationMap( 204 ((ASN1Sequence) seq.getObjectAt(TEE_ENFORCED_INDEX)).toArray()); 205 } 206 securityLevelToEnum(int securityLevel)207 private static SecurityLevel securityLevelToEnum(int securityLevel) { 208 switch (securityLevel) { 209 case KM_SECURITY_LEVEL_SOFTWARE: 210 return SecurityLevel.SOFTWARE; 211 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 212 return SecurityLevel.TRUSTED_ENVIRONMENT; 213 case KM_SECURITY_LEVEL_STRONG_BOX: 214 return SecurityLevel.STRONG_BOX; 215 default: 216 throw new IllegalArgumentException("Invalid security level."); 217 } 218 } 219 } 220