1 /* Copyright 2019, The Android Open Source Project, Inc. 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.google.android.attestation; 17 18 import static com.google.android.attestation.Constants.ATTESTATION_CHALLENGE_INDEX; 19 import static com.google.android.attestation.Constants.ATTESTATION_SECURITY_LEVEL_INDEX; 20 import static com.google.android.attestation.Constants.ATTESTATION_VERSION_INDEX; 21 import static com.google.android.attestation.Constants.KEYMASTER_SECURITY_LEVEL_INDEX; 22 import static com.google.android.attestation.Constants.KEYMASTER_VERSION_INDEX; 23 import static com.google.android.attestation.Constants.KEY_DESCRIPTION_OID; 24 import static com.google.android.attestation.Constants.KM_SECURITY_LEVEL_SOFTWARE; 25 import static com.google.android.attestation.Constants.KM_SECURITY_LEVEL_STRONG_BOX; 26 import static com.google.android.attestation.Constants.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; 27 import static com.google.android.attestation.Constants.SW_ENFORCED_INDEX; 28 import static com.google.android.attestation.Constants.TEE_ENFORCED_INDEX; 29 import static com.google.android.attestation.Constants.UNIQUE_ID_INDEX; 30 31 import java.io.IOException; 32 import java.security.cert.X509Certificate; 33 import java.util.List; 34 import org.bouncycastle.asn1.ASN1Encodable; 35 import org.bouncycastle.asn1.ASN1Enumerated; 36 import org.bouncycastle.asn1.ASN1InputStream; 37 import org.bouncycastle.asn1.ASN1Integer; 38 import org.bouncycastle.asn1.ASN1OctetString; 39 import org.bouncycastle.asn1.ASN1Sequence; 40 import org.bouncycastle.asn1.DEROctetString; 41 import org.bouncycastle.asn1.DERSequence; 42 43 /** Java representation of Key Attestation extension data. */ 44 public class ParsedAttestationRecord { 45 46 public final int attestationVersion; 47 public final SecurityLevel attestationSecurityLevel; 48 public final int keymasterVersion; 49 public final SecurityLevel keymasterSecurityLevel; 50 public final byte[] attestationChallenge; 51 public final byte[] uniqueId; 52 public final AuthorizationList softwareEnforced; 53 public final AuthorizationList teeEnforced; 54 ParsedAttestationRecord(ASN1Sequence extensionData)55 private ParsedAttestationRecord(ASN1Sequence extensionData) { 56 this.attestationVersion = 57 ASN1Parsing.getIntegerFromAsn1(extensionData.getObjectAt(ATTESTATION_VERSION_INDEX)); 58 this.attestationSecurityLevel = 59 securityLevelToEnum( 60 ASN1Parsing.getIntegerFromAsn1( 61 extensionData.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX))); 62 this.keymasterVersion = 63 ASN1Parsing.getIntegerFromAsn1(extensionData.getObjectAt(KEYMASTER_VERSION_INDEX)); 64 this.keymasterSecurityLevel = 65 securityLevelToEnum( 66 ASN1Parsing.getIntegerFromAsn1( 67 extensionData.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX))); 68 this.attestationChallenge = 69 ((ASN1OctetString) extensionData.getObjectAt(ATTESTATION_CHALLENGE_INDEX)).getOctets(); 70 this.uniqueId = ((ASN1OctetString) extensionData.getObjectAt(UNIQUE_ID_INDEX)).getOctets(); 71 this.softwareEnforced = 72 AuthorizationList.createAuthorizationList( 73 ((ASN1Sequence) extensionData.getObjectAt(SW_ENFORCED_INDEX)).toArray(), 74 attestationVersion); 75 this.teeEnforced = 76 AuthorizationList.createAuthorizationList( 77 ((ASN1Sequence) extensionData.getObjectAt(TEE_ENFORCED_INDEX)).toArray(), 78 attestationVersion); 79 } 80 ParsedAttestationRecord( int attestationVersion, SecurityLevel attestationSecurityLevel, int keymasterVersion, SecurityLevel keymasterSecurityLevel, byte[] attestationChallenge, byte[] uniqueId, AuthorizationList softwareEnforced, AuthorizationList teeEnforced)81 private ParsedAttestationRecord( 82 int attestationVersion, 83 SecurityLevel attestationSecurityLevel, 84 int keymasterVersion, 85 SecurityLevel keymasterSecurityLevel, 86 byte[] attestationChallenge, 87 byte[] uniqueId, 88 AuthorizationList softwareEnforced, 89 AuthorizationList teeEnforced) { 90 this.attestationVersion = attestationVersion; 91 this.attestationSecurityLevel = attestationSecurityLevel; 92 this.keymasterVersion = keymasterVersion; 93 this.keymasterSecurityLevel = keymasterSecurityLevel; 94 this.attestationChallenge = attestationChallenge; 95 this.uniqueId = uniqueId; 96 this.softwareEnforced = softwareEnforced; 97 this.teeEnforced = teeEnforced; 98 } 99 createParsedAttestationRecord(List<X509Certificate> certs)100 public static ParsedAttestationRecord createParsedAttestationRecord(List<X509Certificate> certs) 101 throws IOException { 102 103 // Parse the attestation record that is closest to the root. This prevents an adversary from 104 // attesting an attestation record of their choice with an otherwise trusted chain using the 105 // following attack: 106 // 1) having the TEE attest a key under the adversary's control, 107 // 2) using that key to sign a new leaf certificate with an attestation extension that has their chosen attestation record, then 108 // 3) appending that certificate to the original certificate chain. 109 for (int i = certs.size() - 1; i >= 0; i--) { 110 byte[] attestationExtensionBytes = certs.get(i).getExtensionValue(KEY_DESCRIPTION_OID); 111 if (attestationExtensionBytes != null && attestationExtensionBytes.length != 0) { 112 return new ParsedAttestationRecord(extractAttestationSequence(attestationExtensionBytes)); 113 } 114 } 115 116 throw new IllegalArgumentException("Couldn't find the keystore attestation extension data."); 117 } 118 create(ASN1Sequence extensionData)119 public static ParsedAttestationRecord create(ASN1Sequence extensionData) { 120 return new ParsedAttestationRecord(extensionData); 121 } 122 create( int attestationVersion, SecurityLevel attestationSecurityLevel, int keymasterVersion, SecurityLevel keymasterSecurityLevel, byte[] attestationChallenge, byte[] uniqueId, AuthorizationList softwareEnforced, AuthorizationList teeEnforced)123 public static ParsedAttestationRecord create( 124 int attestationVersion, 125 SecurityLevel attestationSecurityLevel, 126 int keymasterVersion, 127 SecurityLevel keymasterSecurityLevel, 128 byte[] attestationChallenge, 129 byte[] uniqueId, 130 AuthorizationList softwareEnforced, 131 AuthorizationList teeEnforced) { 132 return new ParsedAttestationRecord( 133 attestationVersion, 134 attestationSecurityLevel, 135 keymasterVersion, 136 keymasterSecurityLevel, 137 attestationChallenge, 138 uniqueId, 139 softwareEnforced, 140 teeEnforced); 141 } 142 securityLevelToEnum(int securityLevel)143 private static SecurityLevel securityLevelToEnum(int securityLevel) { 144 switch (securityLevel) { 145 case KM_SECURITY_LEVEL_SOFTWARE: 146 return SecurityLevel.SOFTWARE; 147 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 148 return SecurityLevel.TRUSTED_ENVIRONMENT; 149 case KM_SECURITY_LEVEL_STRONG_BOX: 150 return SecurityLevel.STRONG_BOX; 151 default: 152 throw new IllegalArgumentException("Invalid security level."); 153 } 154 } 155 securityLevelToInt(SecurityLevel securityLevel)156 private static int securityLevelToInt(SecurityLevel securityLevel) { 157 switch (securityLevel) { 158 case SOFTWARE: 159 return KM_SECURITY_LEVEL_SOFTWARE; 160 case TRUSTED_ENVIRONMENT: 161 return KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; 162 case STRONG_BOX: 163 return KM_SECURITY_LEVEL_STRONG_BOX; 164 } 165 throw new IllegalArgumentException("Invalid security level."); 166 } 167 extractAttestationSequence(byte[] attestationExtensionBytes)168 private static ASN1Sequence extractAttestationSequence(byte[] attestationExtensionBytes) 169 throws IOException { 170 ASN1Sequence decodedSequence; 171 try (ASN1InputStream asn1InputStream = new ASN1InputStream(attestationExtensionBytes)) { 172 // The extension contains one object, a sequence, in the 173 // Distinguished Encoding Rules (DER)-encoded form. Get the DER 174 // bytes. 175 byte[] derSequenceBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets(); 176 // Decode the bytes as an ASN1 sequence object. 177 try (ASN1InputStream seqInputStream = new ASN1InputStream(derSequenceBytes)) { 178 decodedSequence = (ASN1Sequence) seqInputStream.readObject(); 179 } 180 } 181 return decodedSequence; 182 } 183 toAsn1Sequence()184 public ASN1Sequence toAsn1Sequence() { 185 ASN1Encodable[] vector = new ASN1Encodable[8]; 186 vector[ATTESTATION_VERSION_INDEX] = new ASN1Integer(this.attestationVersion); 187 vector[ATTESTATION_SECURITY_LEVEL_INDEX] = 188 new ASN1Enumerated(securityLevelToInt(this.attestationSecurityLevel)); 189 vector[KEYMASTER_VERSION_INDEX] = new ASN1Integer(this.keymasterVersion); 190 vector[KEYMASTER_SECURITY_LEVEL_INDEX] = 191 new ASN1Enumerated(securityLevelToInt(this.keymasterSecurityLevel)); 192 vector[ATTESTATION_CHALLENGE_INDEX] = new DEROctetString(this.attestationChallenge); 193 vector[UNIQUE_ID_INDEX] = new DEROctetString(this.uniqueId); 194 if (this.softwareEnforced != null) { 195 vector[SW_ENFORCED_INDEX] = this.softwareEnforced.toAsn1Sequence(); 196 } 197 if (this.teeEnforced != null) { 198 vector[TEE_ENFORCED_INDEX] = this.teeEnforced.toAsn1Sequence(); 199 } 200 return new DERSequence(vector); 201 } 202 203 /** 204 * This indicates the extent to which a software feature, such as a key pair, is protected based 205 * on its location within the device. 206 */ 207 public enum SecurityLevel { 208 SOFTWARE, 209 TRUSTED_ENVIRONMENT, 210 STRONG_BOX 211 } 212 } 213