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 org.bouncycastle.asn1.ASN1Encodable; 34 import org.bouncycastle.asn1.ASN1Enumerated; 35 import org.bouncycastle.asn1.ASN1InputStream; 36 import org.bouncycastle.asn1.ASN1Integer; 37 import org.bouncycastle.asn1.ASN1OctetString; 38 import org.bouncycastle.asn1.ASN1Sequence; 39 import org.bouncycastle.asn1.DEROctetString; 40 import org.bouncycastle.asn1.DERSequence; 41 42 /** Java representation of Key Attestation extension data. */ 43 public class ParsedAttestationRecord { 44 45 public final int attestationVersion; 46 public final SecurityLevel attestationSecurityLevel; 47 public final int keymasterVersion; 48 public final SecurityLevel keymasterSecurityLevel; 49 public final byte[] attestationChallenge; 50 public final byte[] uniqueId; 51 public final AuthorizationList softwareEnforced; 52 public final AuthorizationList teeEnforced; 53 ParsedAttestationRecord(ASN1Sequence extensionData)54 private ParsedAttestationRecord(ASN1Sequence extensionData) { 55 this.attestationVersion = 56 ASN1Parsing.getIntegerFromAsn1(extensionData.getObjectAt(ATTESTATION_VERSION_INDEX)); 57 this.attestationSecurityLevel = 58 securityLevelToEnum( 59 ASN1Parsing.getIntegerFromAsn1( 60 extensionData.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX))); 61 this.keymasterVersion = 62 ASN1Parsing.getIntegerFromAsn1(extensionData.getObjectAt(KEYMASTER_VERSION_INDEX)); 63 this.keymasterSecurityLevel = 64 securityLevelToEnum( 65 ASN1Parsing.getIntegerFromAsn1( 66 extensionData.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX))); 67 this.attestationChallenge = 68 ((ASN1OctetString) extensionData.getObjectAt(ATTESTATION_CHALLENGE_INDEX)).getOctets(); 69 this.uniqueId = ((ASN1OctetString) extensionData.getObjectAt(UNIQUE_ID_INDEX)).getOctets(); 70 this.softwareEnforced = 71 AuthorizationList.createAuthorizationList( 72 ((ASN1Sequence) extensionData.getObjectAt(SW_ENFORCED_INDEX)).toArray(), 73 attestationVersion); 74 this.teeEnforced = 75 AuthorizationList.createAuthorizationList( 76 ((ASN1Sequence) extensionData.getObjectAt(TEE_ENFORCED_INDEX)).toArray(), 77 attestationVersion); 78 } 79 ParsedAttestationRecord( int attestationVersion, SecurityLevel attestationSecurityLevel, int keymasterVersion, SecurityLevel keymasterSecurityLevel, byte[] attestationChallenge, byte[] uniqueId, AuthorizationList softwareEnforced, AuthorizationList teeEnforced)80 private ParsedAttestationRecord( 81 int attestationVersion, 82 SecurityLevel attestationSecurityLevel, 83 int keymasterVersion, 84 SecurityLevel keymasterSecurityLevel, 85 byte[] attestationChallenge, 86 byte[] uniqueId, 87 AuthorizationList softwareEnforced, 88 AuthorizationList teeEnforced) { 89 this.attestationVersion = attestationVersion; 90 this.attestationSecurityLevel = attestationSecurityLevel; 91 this.keymasterVersion = keymasterVersion; 92 this.keymasterSecurityLevel = keymasterSecurityLevel; 93 this.attestationChallenge = attestationChallenge; 94 this.uniqueId = uniqueId; 95 this.softwareEnforced = softwareEnforced; 96 this.teeEnforced = teeEnforced; 97 } 98 createParsedAttestationRecord(X509Certificate cert)99 public static ParsedAttestationRecord createParsedAttestationRecord(X509Certificate cert) 100 throws IOException { 101 ASN1Sequence extensionData = extractAttestationSequence(cert); 102 return new ParsedAttestationRecord(extensionData); 103 } 104 create(ASN1Sequence extensionData)105 public static ParsedAttestationRecord create(ASN1Sequence extensionData) { 106 return new ParsedAttestationRecord(extensionData); 107 } 108 create( int attestationVersion, SecurityLevel attestationSecurityLevel, int keymasterVersion, SecurityLevel keymasterSecurityLevel, byte[] attestationChallenge, byte[] uniqueId, AuthorizationList softwareEnforced, AuthorizationList teeEnforced)109 public static ParsedAttestationRecord create( 110 int attestationVersion, 111 SecurityLevel attestationSecurityLevel, 112 int keymasterVersion, 113 SecurityLevel keymasterSecurityLevel, 114 byte[] attestationChallenge, 115 byte[] uniqueId, 116 AuthorizationList softwareEnforced, 117 AuthorizationList teeEnforced) { 118 return new ParsedAttestationRecord( 119 attestationVersion, 120 attestationSecurityLevel, 121 keymasterVersion, 122 keymasterSecurityLevel, 123 attestationChallenge, 124 uniqueId, 125 softwareEnforced, 126 teeEnforced); 127 } 128 securityLevelToEnum(int securityLevel)129 private static SecurityLevel securityLevelToEnum(int securityLevel) { 130 switch (securityLevel) { 131 case KM_SECURITY_LEVEL_SOFTWARE: 132 return SecurityLevel.SOFTWARE; 133 case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT: 134 return SecurityLevel.TRUSTED_ENVIRONMENT; 135 case KM_SECURITY_LEVEL_STRONG_BOX: 136 return SecurityLevel.STRONG_BOX; 137 default: 138 throw new IllegalArgumentException("Invalid security level."); 139 } 140 } 141 securityLevelToInt(SecurityLevel securityLevel)142 private static int securityLevelToInt(SecurityLevel securityLevel) { 143 switch (securityLevel) { 144 case SOFTWARE: 145 return KM_SECURITY_LEVEL_SOFTWARE; 146 case TRUSTED_ENVIRONMENT: 147 return KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT; 148 case STRONG_BOX: 149 return KM_SECURITY_LEVEL_STRONG_BOX; 150 } 151 throw new IllegalArgumentException("Invalid security level."); 152 } 153 extractAttestationSequence(X509Certificate attestationCert)154 private static ASN1Sequence extractAttestationSequence(X509Certificate attestationCert) 155 throws IOException { 156 byte[] attestationExtensionBytes = attestationCert.getExtensionValue(KEY_DESCRIPTION_OID); 157 if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) { 158 throw new IllegalArgumentException("Couldn't find the keystore attestation extension data."); 159 } 160 161 ASN1Sequence decodedSequence; 162 try (ASN1InputStream asn1InputStream = new ASN1InputStream(attestationExtensionBytes)) { 163 // The extension contains one object, a sequence, in the 164 // Distinguished Encoding Rules (DER)-encoded form. Get the DER 165 // bytes. 166 byte[] derSequenceBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets(); 167 // Decode the bytes as an ASN1 sequence object. 168 try (ASN1InputStream seqInputStream = new ASN1InputStream(derSequenceBytes)) { 169 decodedSequence = (ASN1Sequence) seqInputStream.readObject(); 170 } 171 } 172 return decodedSequence; 173 } 174 toAsn1Sequence()175 public ASN1Sequence toAsn1Sequence() { 176 ASN1Encodable[] vector = new ASN1Encodable[8]; 177 vector[ATTESTATION_VERSION_INDEX] = new ASN1Integer(this.attestationVersion); 178 vector[ATTESTATION_SECURITY_LEVEL_INDEX] = 179 new ASN1Enumerated(securityLevelToInt(this.attestationSecurityLevel)); 180 vector[KEYMASTER_VERSION_INDEX] = new ASN1Integer(this.keymasterVersion); 181 vector[KEYMASTER_SECURITY_LEVEL_INDEX] = 182 new ASN1Enumerated(securityLevelToInt(this.keymasterSecurityLevel)); 183 vector[ATTESTATION_CHALLENGE_INDEX] = new DEROctetString(this.attestationChallenge); 184 vector[UNIQUE_ID_INDEX] = new DEROctetString(this.uniqueId); 185 if (this.softwareEnforced != null) { 186 vector[SW_ENFORCED_INDEX] = this.softwareEnforced.toAsn1Sequence(); 187 } 188 if (this.teeEnforced != null) { 189 vector[TEE_ENFORCED_INDEX] = this.teeEnforced.toAsn1Sequence(); 190 } 191 return new DERSequence(vector); 192 } 193 194 /** 195 * This indicates the extent to which a software feature, such as a key pair, is protected based 196 * on its location within the device. 197 */ 198 public enum SecurityLevel { 199 SOFTWARE, 200 TRUSTED_ENVIRONMENT, 201 STRONG_BOX 202 } 203 } 204