• 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 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