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 org.bouncycastle.asn1.ASN1Sequence; 24 25 import java.security.cert.CertificateParsingException; 26 import java.security.cert.X509Certificate; 27 import java.util.Set; 28 import java.util.stream.Collectors; 29 30 /** 31 * Parses an attestation certificate and provides an easy-to-use interface for examining the 32 * contents. 33 */ 34 public class Attestation { 35 static final String KEY_DESCRIPTION_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 static final int ATTESTATION_VERSION_INDEX = 0; 38 static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1; 39 static final int KEYMASTER_VERSION_INDEX = 2; 40 static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3; 41 static final int ATTESTATION_CHALLENGE_INDEX = 4; 42 static final int UNIQUE_ID_INDEX = 5; 43 static final int SW_ENFORCED_INDEX = 6; 44 static final int TEE_ENFORCED_INDEX = 7; 45 46 public static final int KM_SECURITY_LEVEL_SOFTWARE = 0; 47 public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1; 48 public static final int KM_SECURITY_LEVEL_STRONG_BOX = 2; 49 50 private final int attestationVersion; 51 private final int attestationSecurityLevel; 52 private final int keymasterVersion; 53 private final int keymasterSecurityLevel; 54 private final byte[] attestationChallenge; 55 private final byte[] uniqueId; 56 private final AuthorizationList softwareEnforced; 57 private final AuthorizationList teeEnforced; 58 private final Set<String> unexpectedExtensionOids; 59 60 61 /** 62 * Constructs an {@code Attestation} object from the provided {@link X509Certificate}, 63 * extracting the attestation data from the attestation extension. 64 * 65 * @throws CertificateParsingException if the certificate does not contain a properly-formatted 66 * attestation extension. 67 */ Attestation(X509Certificate x509Cert)68 public Attestation(X509Certificate x509Cert) throws CertificateParsingException { 69 ASN1Sequence seq = getAttestationSequence(x509Cert); 70 unexpectedExtensionOids = retrieveUnexpectedExtensionOids(x509Cert); 71 72 attestationVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX)); 73 attestationSecurityLevel = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX)); 74 keymasterVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX)); 75 keymasterSecurityLevel = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX)); 76 77 attestationChallenge = 78 Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.ATTESTATION_CHALLENGE_INDEX)); 79 80 uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.UNIQUE_ID_INDEX)); 81 82 softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX)); 83 teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX)); 84 } 85 securityLevelToString(int attestationSecurityLevel)86 public static String securityLevelToString(int attestationSecurityLevel) { 87 switch (attestationSecurityLevel) { 88 case KM_SECURITY_LEVEL_SOFTWARE: 89 return "Software"; 90 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 91 return "TEE"; 92 case KM_SECURITY_LEVEL_STRONG_BOX: 93 return "StrongBox"; 94 default: 95 return "Unkown"; 96 } 97 } 98 getAttestationVersion()99 public int getAttestationVersion() { 100 return attestationVersion; 101 } 102 getAttestationSecurityLevel()103 public int getAttestationSecurityLevel() { 104 return attestationSecurityLevel; 105 } 106 getKeymasterVersion()107 public int getKeymasterVersion() { 108 return keymasterVersion; 109 } 110 getKeymasterSecurityLevel()111 public int getKeymasterSecurityLevel() { 112 return keymasterSecurityLevel; 113 } 114 getAttestationChallenge()115 public byte[] getAttestationChallenge() { 116 return attestationChallenge; 117 } 118 getUniqueId()119 public byte[] getUniqueId() { 120 return uniqueId; 121 } 122 getSoftwareEnforced()123 public AuthorizationList getSoftwareEnforced() { 124 return softwareEnforced; 125 } 126 getTeeEnforced()127 public AuthorizationList getTeeEnforced() { 128 return teeEnforced; 129 } 130 getUnexpectedExtensionOids()131 public Set<String> getUnexpectedExtensionOids() { 132 return unexpectedExtensionOids; 133 } 134 135 @Override toString()136 public String toString() { 137 StringBuilder s = new StringBuilder(); 138 s.append("Attest version: " + attestationVersion); 139 s.append("\nAttest security: " + securityLevelToString(attestationSecurityLevel)); 140 s.append("\nKM version: " + keymasterVersion); 141 s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel)); 142 143 s.append("\nChallenge"); 144 String stringChallenge = new String(attestationChallenge); 145 if (CharMatcher.ascii().matchesAllOf(stringChallenge)) { 146 s.append(": [" + stringChallenge + "]"); 147 } else { 148 s.append(" (base64): [" + BaseEncoding.base64().encode(attestationChallenge) + "]"); 149 } 150 if (uniqueId != null) { 151 s.append("\nUnique ID (base64): [" + BaseEncoding.base64().encode(uniqueId) + "]"); 152 } 153 154 s.append("\n-- SW enforced --"); 155 s.append(softwareEnforced); 156 s.append("\n-- TEE enforced --"); 157 s.append(teeEnforced); 158 159 return s.toString(); 160 } 161 getAttestationSequence(X509Certificate x509Cert)162 private ASN1Sequence getAttestationSequence(X509Certificate x509Cert) 163 throws CertificateParsingException { 164 byte[] attestationExtensionBytes = x509Cert.getExtensionValue(KEY_DESCRIPTION_OID); 165 if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) { 166 throw new CertificateParsingException( 167 "Did not find extension with OID " + KEY_DESCRIPTION_OID); 168 } 169 return Asn1Utils.getAsn1SequenceFromBytes(attestationExtensionBytes); 170 } 171 retrieveUnexpectedExtensionOids(X509Certificate x509Cert)172 private Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) { 173 return new ImmutableSet.Builder<String>() 174 .addAll(x509Cert.getCriticalExtensionOIDs() 175 .stream() 176 .filter(s -> !KEY_USAGE_OID.equals(s)) 177 .iterator()) 178 .addAll(x509Cert.getNonCriticalExtensionOIDs() 179 .stream() 180 .filter(s -> !KEY_DESCRIPTION_OID.equals(s)) 181 .iterator()) 182 .build(); 183 } 184 } 185