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 56 int attestationVersion; 57 int keymasterVersion; 58 int keymasterSecurityLevel; 59 byte[] attestationChallenge; 60 byte[] uniqueId; 61 AuthorizationList softwareEnforced; 62 AuthorizationList teeEnforced; 63 Set<String> unexpectedExtensionOids; 64 65 /** 66 * Constructs an {@code Attestation} object from the provided {@link X509Certificate}, 67 * extracting the attestation data from the attestation extension. 68 * 69 * <p>This method ensures that at most one attestation extension is included in the certificate. 70 * 71 * @throws CertificateParsingException if the certificate does not contain a properly-formatted 72 * attestation extension, if it contains multiple attestation extensions, or if the 73 * attestation extension can not be parsed. 74 */ 75 loadFromCertificate(X509Certificate x509Cert)76 public static Attestation loadFromCertificate(X509Certificate x509Cert) 77 throws CertificateParsingException { 78 return Attestation.loadFromCertificate(x509Cert, true); 79 } loadFromCertificate(X509Certificate x509Cert, boolean strictParsing)80 public static Attestation loadFromCertificate(X509Certificate x509Cert, boolean strictParsing) 81 throws CertificateParsingException { 82 if (x509Cert.getExtensionValue(EAT_OID) == null 83 && x509Cert.getExtensionValue(ASN1_OID) == null) { 84 throw new CertificateParsingException("No attestation extensions found"); 85 } 86 if (x509Cert.getExtensionValue(EAT_OID) != null) { 87 if (x509Cert.getExtensionValue(ASN1_OID) != null) { 88 throw new CertificateParsingException("Multiple attestation extensions found"); 89 } 90 try { 91 return new EatAttestation(x509Cert); 92 } catch (CborException cbe) { 93 throw new CertificateParsingException("Unable to parse EAT extension", cbe); 94 } 95 } 96 Attestation attestation = new Asn1Attestation(x509Cert, strictParsing); 97 if (x509Cert.getExtensionValue(CRL_DP_OID) != null 98 && attestation.getKeymasterVersion() >= KM_VERSION_KEYMINT_3) { 99 throw new CertificateParsingException( 100 "CRL Distribution Points extension found in leaf certificate."); 101 } 102 return attestation; 103 } 104 Attestation(X509Certificate x509Cert)105 Attestation(X509Certificate x509Cert) { 106 unexpectedExtensionOids = retrieveUnexpectedExtensionOids(x509Cert); 107 } 108 securityLevelToString(int attestationSecurityLevel)109 public static String securityLevelToString(int attestationSecurityLevel) { 110 switch (attestationSecurityLevel) { 111 case KM_SECURITY_LEVEL_SOFTWARE: 112 return "Software"; 113 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 114 return "TEE"; 115 case KM_SECURITY_LEVEL_STRONG_BOX: 116 return "StrongBox"; 117 default: 118 return "Unkown"; 119 } 120 } 121 getAttestationVersion()122 public int getAttestationVersion() { 123 return attestationVersion; 124 } 125 getAttestationSecurityLevel()126 public abstract int getAttestationSecurityLevel(); 127 getRootOfTrust()128 public abstract RootOfTrust getRootOfTrust(); 129 130 // Returns one of the KM_VERSION_* values define above. getKeymasterVersion()131 public int getKeymasterVersion() { 132 return keymasterVersion; 133 } 134 getKeymasterSecurityLevel()135 public int getKeymasterSecurityLevel() { 136 return keymasterSecurityLevel; 137 } 138 getAttestationChallenge()139 public byte[] getAttestationChallenge() { 140 return attestationChallenge; 141 } 142 getUniqueId()143 public byte[] getUniqueId() { 144 return uniqueId; 145 } 146 getSoftwareEnforced()147 public AuthorizationList getSoftwareEnforced() { 148 return softwareEnforced; 149 } 150 getTeeEnforced()151 public AuthorizationList getTeeEnforced() { 152 return teeEnforced; 153 } 154 getUnexpectedExtensionOids()155 public Set<String> getUnexpectedExtensionOids() { 156 return unexpectedExtensionOids; 157 } 158 159 @Override toString()160 public String toString() { 161 StringBuilder s = new StringBuilder(); 162 s.append("Extension type: " + getClass()); 163 s.append("\nAttest version: " + attestationVersion); 164 s.append("\nAttest security: " + securityLevelToString(getAttestationSecurityLevel())); 165 s.append("\nKM version: " + keymasterVersion); 166 s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel)); 167 168 s.append("\nChallenge"); 169 String stringChallenge = 170 attestationChallenge != null ? new String(attestationChallenge) : "null"; 171 if (CharMatcher.ascii().matchesAllOf(stringChallenge)) { 172 s.append(": [" + stringChallenge + "]"); 173 } else { 174 s.append(" (base64): [" + BaseEncoding.base64().encode(attestationChallenge) + "]"); 175 } 176 if (uniqueId != null) { 177 s.append("\nUnique ID (base64): [" + BaseEncoding.base64().encode(uniqueId) + "]"); 178 } 179 180 s.append("\n-- SW enforced --"); 181 s.append(softwareEnforced); 182 s.append("\n-- TEE enforced --"); 183 s.append(teeEnforced); 184 185 return s.toString(); 186 } 187 retrieveUnexpectedExtensionOids(X509Certificate x509Cert)188 Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) { 189 return new ImmutableSet.Builder<String>() 190 .addAll( 191 x509Cert.getCriticalExtensionOIDs().stream() 192 .filter(s -> !KEY_USAGE_OID.equals(s)) 193 .iterator()) 194 .addAll( 195 x509Cert.getNonCriticalExtensionOIDs().stream() 196 .filter(s -> !ASN1_OID.equals(s) && !EAT_OID.equals(s)) 197 .iterator()) 198 .build(); 199 } 200 } 201