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