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 com.google.attestationexample; 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 public static final int KM_SECURITY_LEVEL_STRONGBOX = 2; 45 46 private final boolean haveAttestation; 47 private final int attestationVersion; 48 private final int attestationSecurityLevel; 49 private final int keymasterVersion; 50 private final int keymasterSecurityLevel; 51 private final byte[] attestationChallenge; 52 private final byte[] uniqueId; 53 private final AuthorizationList softwareEnforced; 54 private final AuthorizationList teeEnforced; 55 56 57 /** 58 * Constructs an {@code Attestation} object from the provided {@link X509Certificate}, 59 * extracting the attestation data from the attestation extension. 60 * 61 * @throws CertificateParsingException if the certificate does not contain a properly-formatted 62 * attestation extension. 63 */ Attestation(X509Certificate x509Cert)64 public Attestation(X509Certificate x509Cert) throws CertificateParsingException { 65 ASN1Sequence seq = getAttestationSequence(x509Cert); 66 if (seq == null) { 67 haveAttestation = false; 68 attestationVersion = 0; 69 attestationSecurityLevel = 0; 70 keymasterVersion = 0; 71 keymasterSecurityLevel = 0; 72 attestationChallenge = null; 73 uniqueId = null; 74 softwareEnforced = null; 75 teeEnforced = null; 76 return; 77 } 78 79 haveAttestation = true; 80 attestationVersion = 81 Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX)); 82 attestationSecurityLevel = 83 Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX)); 84 keymasterVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX)); 85 keymasterSecurityLevel = 86 Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX)); 87 88 attestationChallenge = 89 Asn1Utils.getByteArrayFromAsn1( 90 seq.getObjectAt(Attestation.ATTESTATION_CHALLENGE_INDEX)); 91 92 uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.UNIQUE_ID_INDEX)); 93 94 softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX)); 95 teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX)); 96 } 97 securityLevelToString(int attestationSecurityLevel)98 public static String securityLevelToString(int attestationSecurityLevel) { 99 switch (attestationSecurityLevel) { 100 case KM_SECURITY_LEVEL_SOFTWARE: 101 return "Software"; 102 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 103 return "TEE"; 104 case KM_SECURITY_LEVEL_STRONGBOX: 105 return "StrongBox"; 106 default: 107 return "Unknown"; 108 } 109 } 110 getAttestationVersion()111 public int getAttestationVersion() { 112 return attestationVersion; 113 } 114 getAttestationSecurityLevel()115 public int getAttestationSecurityLevel() { 116 return attestationSecurityLevel; 117 } 118 getKeymasterVersion()119 public int getKeymasterVersion() { 120 return keymasterVersion; 121 } 122 getKeymasterSecurityLevel()123 public int getKeymasterSecurityLevel() { 124 return keymasterSecurityLevel; 125 } 126 getAttestationChallenge()127 public byte[] getAttestationChallenge() { 128 return attestationChallenge; 129 } 130 getUniqueId()131 public byte[] getUniqueId() { 132 return uniqueId; 133 } 134 getSoftwareEnforced()135 public AuthorizationList getSoftwareEnforced() { 136 return softwareEnforced; 137 } 138 getTeeEnforced()139 public AuthorizationList getTeeEnforced() { 140 return teeEnforced; 141 } 142 143 @Override toString()144 public String toString() { 145 if (!haveAttestation) { 146 return "No attestation"; 147 } 148 149 StringBuilder s = new StringBuilder(); 150 s.append("Attestation version: " + attestationVersion); 151 s.append("\nAttestation security: " + securityLevelToString(attestationSecurityLevel)); 152 s.append("\nKM version: " + keymasterVersion); 153 s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel)); 154 155 s.append("\nChallenge"); 156 String stringChallenge = new String(attestationChallenge); 157 if (CharMatcher.ascii().matchesAllOf(stringChallenge)) { 158 s.append(": [" + stringChallenge + "]"); 159 } else { 160 s.append(" (base64): [" + BaseEncoding.base64().encode(attestationChallenge) + "]"); 161 } 162 if (uniqueId != null) { 163 s.append("\nUnique ID (base64): [" + BaseEncoding.base64().encode(uniqueId) + "]"); 164 } 165 166 s.append("\n\n-- SW enforced --"); 167 s.append(softwareEnforced); 168 s.append("\n\n-- TEE enforced --"); 169 s.append(teeEnforced); 170 s.append("\n"); 171 172 return s.toString(); 173 } 174 getAttestationSequence(X509Certificate x509Cert)175 private ASN1Sequence getAttestationSequence(X509Certificate x509Cert) 176 throws CertificateParsingException { 177 byte[] attestationExtensionBytes = x509Cert.getExtensionValue(KEY_DESCRIPTION_OID); 178 if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) { 179 return null; 180 } 181 return Asn1Utils.getAsn1SequenceFromBytes(attestationExtensionBytes); 182 } 183 184 } 185