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