1 /*
2  * Copyright 2020 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 android.security.identity.cts;
18 
19 import java.io.IOException;
20 import java.security.cert.X509Certificate;
21 import java.util.Map;
22 import java.util.HashMap;
23 import java.util.Set;
24 import java.util.Optional;
25 
26 import org.bouncycastle.asn1.ASN1InputStream;
27 import org.bouncycastle.asn1.ASN1OctetString;
28 import org.bouncycastle.asn1.ASN1Sequence;
29 import org.bouncycastle.asn1.ASN1Boolean;
30 import org.bouncycastle.asn1.ASN1Encodable;
31 import org.bouncycastle.asn1.ASN1Enumerated;
32 import org.bouncycastle.asn1.ASN1Integer;
33 import org.bouncycastle.asn1.ASN1OctetString;
34 import org.bouncycastle.asn1.ASN1Primitive;
35 import org.bouncycastle.asn1.ASN1Set;
36 import org.bouncycastle.asn1.ASN1TaggedObject;
37 import org.bouncycastle.asn1.DEROctetString;
38 
39 // This code is loosely based on https://github.com/google/android-key-attestation
40 
41 class ParsedAttestationRecord {
42     private static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";
43 
44     enum SecurityLevel {
45         SOFTWARE,
46         TRUSTED_ENVIRONMENT,
47         STRONG_BOX
48     }
49 
50     private static final int ATTESTATION_VERSION_INDEX = 0;
51     private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
52     private static final int KEYMASTER_VERSION_INDEX = 2;
53     private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
54     private static final int ATTESTATION_CHALLENGE_INDEX = 4;
55     private static final int UNIQUE_ID_INDEX = 5;
56     private static final int SW_ENFORCED_INDEX = 6;
57     private static final int TEE_ENFORCED_INDEX = 7;
58 
59     // Some security values. The complete list is in this AOSP file:
60     // hardware/libhardware/include/hardware/keymaster_defs.h
61     private static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
62     private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
63     private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
64 
65     private int attestationVersion;
66     private SecurityLevel attestationSecurityLevel;
67     private int keymasterVersion;
68     private SecurityLevel keymasterSecurityLevel;
69     private byte[] attestationChallenge;
70     private byte[] uniqueId;
71 
72     private Map<Integer, ASN1Primitive> softwareEnforcedAuthorizations;
73     private Map<Integer, ASN1Primitive> teeEnforcedAuthorizations;
74 
getAttestationVersion()75     public int getAttestationVersion() {
76         return attestationVersion;
77     }
78 
getAttestationSecurityLevel()79     public SecurityLevel getAttestationSecurityLevel() {
80         return attestationSecurityLevel;
81     }
82 
getKeymasterVersion()83     public int getKeymasterVersion() {
84         return keymasterVersion;
85     }
86 
getKeymasterSecurityLevel()87     public SecurityLevel getKeymasterSecurityLevel() {
88         return attestationSecurityLevel;
89     }
90 
getAttestationChallenge()91     public byte[] getAttestationChallenge() {
92         return attestationChallenge;
93     }
94 
getUniqueId()95     public byte[] getUniqueId() {
96         return uniqueId;
97     }
98 
getSoftwareEnforcedAuthorizationTags()99     public Set<Integer> getSoftwareEnforcedAuthorizationTags() {
100         return softwareEnforcedAuthorizations.keySet();
101     }
102 
getTeeEnforcedAuthorizationTags()103     public Set<Integer> getTeeEnforcedAuthorizationTags() {
104         return teeEnforcedAuthorizations.keySet();
105     }
106 
findAuthorizationListEntry( Map<Integer, ASN1Primitive> authorizationMap, int tag)107     private static ASN1Primitive findAuthorizationListEntry(
108         Map<Integer, ASN1Primitive> authorizationMap, int tag) {
109         return authorizationMap.getOrDefault(tag, null);
110     }
111 
getSoftwareAuthorizationInteger(int tag)112     public Optional<Integer> getSoftwareAuthorizationInteger(int tag) {
113         ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
114         return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1);
115     }
116 
getTeeAuthorizationInteger(int tag)117     public Optional<Integer> getTeeAuthorizationInteger(int tag) {
118         ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
119         return Optional.ofNullable(entry).map(ParsedAttestationRecord::getIntegerFromAsn1);
120     }
getSoftwareAuthorizationBoolean(int tag)121     public boolean getSoftwareAuthorizationBoolean(int tag) {
122         ASN1Primitive entry = findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
123         return entry != null;
124     }
125 
getTeeAuthorizationBoolean(int tag)126     public boolean getTeeAuthorizationBoolean(int tag) {
127         ASN1Primitive entry = findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
128         return entry != null;
129     }
130 
getSoftwareAuthorizationByteString(int tag)131     public Optional<byte[]> getSoftwareAuthorizationByteString(int tag) {
132         ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(softwareEnforcedAuthorizations, tag);
133         return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
134     }
135 
getTeeAuthorizationByteString(int tag)136     public Optional<byte[]> getTeeAuthorizationByteString(int tag) {
137         ASN1OctetString entry = (ASN1OctetString) findAuthorizationListEntry(teeEnforcedAuthorizations, tag);
138         return Optional.ofNullable(entry).map(ASN1OctetString::getOctets);
139     }
140 
getBooleanFromAsn1(ASN1Encodable asn1Value)141     private static boolean getBooleanFromAsn1(ASN1Encodable asn1Value) {
142         if (asn1Value instanceof ASN1Boolean) {
143             return ((ASN1Boolean) asn1Value).isTrue();
144         } else {
145             throw new RuntimeException(
146                 "Boolean value expected; found " + asn1Value.getClass().getName() + " instead.");
147         }
148     }
149 
getIntegerFromAsn1(ASN1Encodable asn1Value)150     private static int getIntegerFromAsn1(ASN1Encodable asn1Value) {
151         if (asn1Value instanceof ASN1Integer) {
152             return ((ASN1Integer) asn1Value).getValue().intValue();
153         } else if (asn1Value instanceof ASN1Enumerated) {
154             return ((ASN1Enumerated) asn1Value).getValue().intValue();
155         } else {
156             throw new IllegalArgumentException(
157                 "Integer value expected; found " + asn1Value.getClass().getName() + " instead.");
158         }
159     }
160 
getAuthorizationMap( ASN1Encodable[] authorizationList)161     private static Map<Integer, ASN1Primitive> getAuthorizationMap(
162         ASN1Encodable[] authorizationList) {
163         Map<Integer, ASN1Primitive> authorizationMap = new HashMap<>();
164         for (ASN1Encodable entry : authorizationList) {
165             ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
166             authorizationMap.put(taggedEntry.getTagNo(), taggedEntry.getObject());
167         }
168         return authorizationMap;
169     }
170 
ParsedAttestationRecord(X509Certificate cert)171     public ParsedAttestationRecord(X509Certificate cert) throws IOException {
172         byte[] attestationExtensionBytes = cert.getExtensionValue(KEY_DESCRIPTION_OID);
173         if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
174             throw new IllegalArgumentException("Couldn't find keystore attestation extension.");
175         }
176 
177         ASN1Sequence seq;
178         try (ASN1InputStream asn1InputStream = new ASN1InputStream(attestationExtensionBytes)) {
179             // The extension contains one object, a sequence, in the
180             // Distinguished Encoding Rules (DER)-encoded form. Get the DER
181             // bytes.
182             byte[] derSequenceBytes = ((ASN1OctetString) asn1InputStream.readObject()).getOctets();
183             // Decode the bytes as an ASN1 sequence object.
184             try (ASN1InputStream seqInputStream = new ASN1InputStream(derSequenceBytes)) {
185                 seq = (ASN1Sequence) seqInputStream.readObject();
186             }
187         }
188 
189         this.attestationVersion = getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
190         this.attestationSecurityLevel =
191                 securityLevelToEnum(getIntegerFromAsn1(
192                     seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX)));
193         this.keymasterVersion = getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
194         this.keymasterSecurityLevel = securityLevelToEnum(
195             getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX)));
196         this.attestationChallenge =
197                 ((ASN1OctetString) seq.getObjectAt(ATTESTATION_CHALLENGE_INDEX)).getOctets();
198         this.uniqueId = ((ASN1OctetString) seq.getObjectAt(UNIQUE_ID_INDEX)).getOctets();
199 
200         this.softwareEnforcedAuthorizations = getAuthorizationMap(
201             ((ASN1Sequence) seq.getObjectAt(SW_ENFORCED_INDEX)).toArray());
202 
203         this.teeEnforcedAuthorizations = getAuthorizationMap(
204             ((ASN1Sequence) seq.getObjectAt(TEE_ENFORCED_INDEX)).toArray());
205     }
206 
securityLevelToEnum(int securityLevel)207     private static SecurityLevel securityLevelToEnum(int securityLevel) {
208         switch (securityLevel) {
209             case KM_SECURITY_LEVEL_SOFTWARE:
210                 return SecurityLevel.SOFTWARE;
211             case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
212                 return SecurityLevel.TRUSTED_ENVIRONMENT;
213             case KM_SECURITY_LEVEL_STRONG_BOX:
214                 return SecurityLevel.STRONG_BOX;
215             default:
216                 throw new IllegalArgumentException("Invalid security level.");
217         }
218     }
219 }
220