1 /* 2 * Copyright (C) 2016 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.keystore.cts; 18 19 import com.google.common.base.CharMatcher; 20 import com.google.common.collect.ImmutableSet; 21 import com.google.common.io.BaseEncoding; 22 23 import java.security.cert.CertificateParsingException; 24 import java.security.cert.X509Certificate; 25 import java.util.Set; 26 27 import co.nstant.in.cbor.CborException; 28 29 /** 30 * Parses an attestation certificate and provides an easy-to-use interface for examining the 31 * contents. 32 */ 33 public abstract class Attestation { 34 static final String EAT_OID = "1.3.6.1.4.1.11129.2.1.25"; 35 static final String ASN1_OID = "1.3.6.1.4.1.11129.2.1.17"; 36 static final String KEY_USAGE_OID = "2.5.29.15"; // Standard key usage extension. 37 38 static final String CRL_DP_OID = "2.5.29.31"; // Standard CRL Distribution Points extension. 39 40 public static final int KM_SECURITY_LEVEL_SOFTWARE = 0; 41 public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1; 42 public static final int KM_SECURITY_LEVEL_STRONG_BOX = 2; 43 44 // Known KeyMaster/KeyMint versions. This is the version number 45 // which appear in the keymasterVersion field. 46 public static final int KM_VERSION_KEYMASTER_1 = 10; 47 public static final int KM_VERSION_KEYMASTER_1_1 = 11; 48 public static final int KM_VERSION_KEYMASTER_2 = 20; 49 public static final int KM_VERSION_KEYMASTER_3 = 30; 50 public static final int KM_VERSION_KEYMASTER_4 = 40; 51 public static final int KM_VERSION_KEYMASTER_4_1 = 41; 52 public static final int KM_VERSION_KEYMINT_1 = 100; 53 public static final int KM_VERSION_KEYMINT_2 = 200; 54 public static final int KM_VERSION_KEYMINT_3 = 300; 55 public static final int KM_VERSION_KEYMINT_4 = 400; 56 57 int attestationVersion; 58 int keymasterVersion; 59 int keymasterSecurityLevel; 60 byte[] attestationChallenge; 61 byte[] uniqueId; 62 AuthorizationList softwareEnforced; 63 AuthorizationList teeEnforced; 64 Set<String> unexpectedExtensionOids; 65 66 /** 67 * Constructs an {@code Attestation} object from the provided {@link X509Certificate}, 68 * extracting the attestation data from the attestation extension. 69 * 70 * <p>This method ensures that at most one attestation extension is included in the certificate. 71 * 72 * @throws CertificateParsingException if the certificate does not contain a properly-formatted 73 * attestation extension, if it contains multiple attestation extensions, or if the 74 * attestation extension can not be parsed. 75 */ 76 loadFromCertificate(X509Certificate x509Cert)77 public static Attestation loadFromCertificate(X509Certificate x509Cert) 78 throws CertificateParsingException { 79 return Attestation.loadFromCertificate(x509Cert, true); 80 } loadFromCertificate(X509Certificate x509Cert, boolean strictParsing)81 public static Attestation loadFromCertificate(X509Certificate x509Cert, boolean strictParsing) 82 throws CertificateParsingException { 83 if (x509Cert.getExtensionValue(EAT_OID) == null 84 && x509Cert.getExtensionValue(ASN1_OID) == null) { 85 throw new CertificateParsingException("No attestation extensions found"); 86 } 87 if (x509Cert.getExtensionValue(EAT_OID) != null) { 88 if (x509Cert.getExtensionValue(ASN1_OID) != null) { 89 throw new CertificateParsingException("Multiple attestation extensions found"); 90 } 91 try { 92 return new EatAttestation(x509Cert); 93 } catch (CborException cbe) { 94 throw new CertificateParsingException("Unable to parse EAT extension", cbe); 95 } 96 } 97 Attestation attestation = new Asn1Attestation(x509Cert, strictParsing); 98 if (x509Cert.getExtensionValue(CRL_DP_OID) != null 99 && attestation.getKeymasterVersion() >= KM_VERSION_KEYMINT_3) { 100 throw new CertificateParsingException( 101 "CRL Distribution Points extension found in leaf certificate."); 102 } 103 return attestation; 104 } 105 Attestation(X509Certificate x509Cert)106 Attestation(X509Certificate x509Cert) { 107 unexpectedExtensionOids = retrieveUnexpectedExtensionOids(x509Cert); 108 } 109 securityLevelToString(int attestationSecurityLevel)110 public static String securityLevelToString(int attestationSecurityLevel) { 111 switch (attestationSecurityLevel) { 112 case KM_SECURITY_LEVEL_SOFTWARE: 113 return "Software"; 114 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 115 return "TEE"; 116 case KM_SECURITY_LEVEL_STRONG_BOX: 117 return "StrongBox"; 118 default: 119 return "Unkown"; 120 } 121 } 122 getAttestationVersion()123 public int getAttestationVersion() { 124 return attestationVersion; 125 } 126 getAttestationSecurityLevel()127 public abstract int getAttestationSecurityLevel(); 128 getRootOfTrust()129 public abstract RootOfTrust getRootOfTrust(); 130 131 // Returns one of the KM_VERSION_* values define above. getKeymasterVersion()132 public int getKeymasterVersion() { 133 return keymasterVersion; 134 } 135 getKeymasterSecurityLevel()136 public int getKeymasterSecurityLevel() { 137 return keymasterSecurityLevel; 138 } 139 getAttestationChallenge()140 public byte[] getAttestationChallenge() { 141 return attestationChallenge; 142 } 143 getUniqueId()144 public byte[] getUniqueId() { 145 return uniqueId; 146 } 147 getSoftwareEnforced()148 public AuthorizationList getSoftwareEnforced() { 149 return softwareEnforced; 150 } 151 getTeeEnforced()152 public AuthorizationList getTeeEnforced() { 153 return teeEnforced; 154 } 155 getUnexpectedExtensionOids()156 public Set<String> getUnexpectedExtensionOids() { 157 return unexpectedExtensionOids; 158 } 159 160 @Override toString()161 public String toString() { 162 StringBuilder s = new StringBuilder(); 163 s.append("Extension type: " + getClass()); 164 s.append("\nAttest version: " + attestationVersion); 165 s.append("\nAttest security: " + securityLevelToString(getAttestationSecurityLevel())); 166 s.append("\nKM version: " + keymasterVersion); 167 s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel)); 168 169 s.append("\nChallenge"); 170 String stringChallenge = 171 attestationChallenge != null ? new String(attestationChallenge) : "null"; 172 if (CharMatcher.ascii().matchesAllOf(stringChallenge)) { 173 s.append(": [" + stringChallenge + "]"); 174 } else { 175 s.append(" (base64): [" + BaseEncoding.base64().encode(attestationChallenge) + "]"); 176 } 177 if (uniqueId != null) { 178 s.append("\nUnique ID (base64): [" + BaseEncoding.base64().encode(uniqueId) + "]"); 179 } 180 181 s.append("\n-- SW enforced --"); 182 s.append(softwareEnforced); 183 s.append("\n-- TEE enforced --"); 184 s.append(teeEnforced); 185 186 return s.toString(); 187 } 188 retrieveUnexpectedExtensionOids(X509Certificate x509Cert)189 Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) { 190 return new ImmutableSet.Builder<String>() 191 .addAll( 192 x509Cert.getCriticalExtensionOIDs().stream() 193 .filter(s -> !KEY_USAGE_OID.equals(s)) 194 .iterator()) 195 .addAll( 196 x509Cert.getNonCriticalExtensionOIDs().stream() 197 .filter(s -> !ASN1_OID.equals(s) && !EAT_OID.equals(s)) 198 .iterator()) 199 .build(); 200 } 201 } 202