• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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