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 if (x509Cert.getExtensionValue(CRL_DP_OID) != null) { 97 throw new CertificateParsingException( 98 "CRL Distribution Points extension found in leaf certificate."); 99 } 100 return new Asn1Attestation(x509Cert, strictParsing); 101 } 102 Attestation(X509Certificate x509Cert)103 Attestation(X509Certificate x509Cert) { 104 unexpectedExtensionOids = retrieveUnexpectedExtensionOids(x509Cert); 105 } 106 securityLevelToString(int attestationSecurityLevel)107 public static String securityLevelToString(int attestationSecurityLevel) { 108 switch (attestationSecurityLevel) { 109 case KM_SECURITY_LEVEL_SOFTWARE: 110 return "Software"; 111 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 112 return "TEE"; 113 case KM_SECURITY_LEVEL_STRONG_BOX: 114 return "StrongBox"; 115 default: 116 return "Unkown"; 117 } 118 } 119 getAttestationVersion()120 public int getAttestationVersion() { 121 return attestationVersion; 122 } 123 getAttestationSecurityLevel()124 public abstract int getAttestationSecurityLevel(); 125 getRootOfTrust()126 public abstract RootOfTrust getRootOfTrust(); 127 128 // Returns one of the KM_VERSION_* values define above. getKeymasterVersion()129 public int getKeymasterVersion() { 130 return keymasterVersion; 131 } 132 getKeymasterSecurityLevel()133 public int getKeymasterSecurityLevel() { 134 return keymasterSecurityLevel; 135 } 136 getAttestationChallenge()137 public byte[] getAttestationChallenge() { 138 return attestationChallenge; 139 } 140 getUniqueId()141 public byte[] getUniqueId() { 142 return uniqueId; 143 } 144 getSoftwareEnforced()145 public AuthorizationList getSoftwareEnforced() { 146 return softwareEnforced; 147 } 148 getTeeEnforced()149 public AuthorizationList getTeeEnforced() { 150 return teeEnforced; 151 } 152 getUnexpectedExtensionOids()153 public Set<String> getUnexpectedExtensionOids() { 154 return unexpectedExtensionOids; 155 } 156 157 @Override toString()158 public String toString() { 159 StringBuilder s = new StringBuilder(); 160 s.append("Extension type: " + getClass()); 161 s.append("\nAttest version: " + attestationVersion); 162 s.append("\nAttest security: " + securityLevelToString(getAttestationSecurityLevel())); 163 s.append("\nKM version: " + keymasterVersion); 164 s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel)); 165 166 s.append("\nChallenge"); 167 String stringChallenge = 168 attestationChallenge != null ? new String(attestationChallenge) : "null"; 169 if (CharMatcher.ascii().matchesAllOf(stringChallenge)) { 170 s.append(": [" + stringChallenge + "]"); 171 } else { 172 s.append(" (base64): [" + BaseEncoding.base64().encode(attestationChallenge) + "]"); 173 } 174 if (uniqueId != null) { 175 s.append("\nUnique ID (base64): [" + BaseEncoding.base64().encode(uniqueId) + "]"); 176 } 177 178 s.append("\n-- SW enforced --"); 179 s.append(softwareEnforced); 180 s.append("\n-- TEE enforced --"); 181 s.append(teeEnforced); 182 183 return s.toString(); 184 } 185 retrieveUnexpectedExtensionOids(X509Certificate x509Cert)186 Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) { 187 return new ImmutableSet.Builder<String>() 188 .addAll( 189 x509Cert.getCriticalExtensionOIDs().stream() 190 .filter(s -> !KEY_USAGE_OID.equals(s)) 191 .iterator()) 192 .addAll( 193 x509Cert.getNonCriticalExtensionOIDs().stream() 194 .filter(s -> !ASN1_OID.equals(s) && !EAT_OID.equals(s)) 195 .iterator()) 196 .build(); 197 } 198 } 199