1 /*
2  * Copyright (C) 2016 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.keystore.cts;
18 
19 import co.nstant.in.cbor.CborException;
20 
21 import com.google.common.base.CharMatcher;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.io.BaseEncoding;
24 
25 import java.security.cert.CertificateParsingException;
26 import java.security.cert.X509Certificate;
27 import java.util.Set;
28 
29 /**
30  * Parses an attestation certificate and provides an easy-to-use interface for examining the
31  * contents.
32  */
33 public abstract class Attestation {
34     static final String EAT_OID = "1.3.6.1.4.1.11129.2.1.25";
35     static final String ASN1_OID = "1.3.6.1.4.1.11129.2.1.17";
36     static final String KEY_USAGE_OID = "2.5.29.15"; // Standard key usage extension.
37 
38     public static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
39     public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
40     public static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
41 
42     // Known KeyMaster/KeyMint versions. This is the version number
43     // which appear in the keymasterVersion field.
44     public static final int KM_VERSION_KEYMASTER_1 = 10;
45     public static final int KM_VERSION_KEYMASTER_1_1 = 11;
46     public static final int KM_VERSION_KEYMASTER_2 = 20;
47     public static final int KM_VERSION_KEYMASTER_3 = 30;
48     public static final int KM_VERSION_KEYMASTER_4 = 40;
49     public static final int KM_VERSION_KEYMASTER_4_1 = 41;
50     public static final int KM_VERSION_KEYMINT_1 = 100;
51 
52     int attestationVersion;
53     int keymasterVersion;
54     int keymasterSecurityLevel;
55     byte[] attestationChallenge;
56     byte[] uniqueId;
57     AuthorizationList softwareEnforced;
58     AuthorizationList teeEnforced;
59     Set<String> unexpectedExtensionOids;
60 
61     /**
62      * Constructs an {@code Attestation} object from the provided {@link X509Certificate},
63      * extracting the attestation data from the attestation extension.
64      *
65      * <p>This method ensures that at most one attestation extension is included in the certificate.
66      *
67      * @throws CertificateParsingException if the certificate does not contain a properly-formatted
68      *     attestation extension, if it contains multiple attestation extensions, or if the
69      *     attestation extension can not be parsed.
70      */
71 
loadFromCertificate(X509Certificate x509Cert)72     public static Attestation loadFromCertificate(X509Certificate x509Cert)
73             throws CertificateParsingException {
74         return Attestation.loadFromCertificate(x509Cert, true);
75     }
loadFromCertificate(X509Certificate x509Cert, boolean strictParsing)76     public static Attestation loadFromCertificate(X509Certificate x509Cert, boolean strictParsing)
77             throws CertificateParsingException {
78         if (x509Cert.getExtensionValue(EAT_OID) == null
79                 && x509Cert.getExtensionValue(ASN1_OID) == null) {
80             throw new CertificateParsingException("No attestation extensions found");
81         }
82         if (x509Cert.getExtensionValue(EAT_OID) != null) {
83             if (x509Cert.getExtensionValue(ASN1_OID) != null) {
84                 throw new CertificateParsingException("Multiple attestation extensions found");
85             }
86             try {
87                 return new EatAttestation(x509Cert);
88             } catch (CborException cbe) {
89                 throw new CertificateParsingException("Unable to parse EAT extension", cbe);
90             }
91         }
92         return new Asn1Attestation(x509Cert, strictParsing);
93     }
94 
Attestation(X509Certificate x509Cert)95     Attestation(X509Certificate x509Cert) {
96         unexpectedExtensionOids = retrieveUnexpectedExtensionOids(x509Cert);
97     }
98 
securityLevelToString(int attestationSecurityLevel)99     public static String securityLevelToString(int attestationSecurityLevel) {
100         switch (attestationSecurityLevel) {
101             case KM_SECURITY_LEVEL_SOFTWARE:
102                 return "Software";
103             case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
104                 return "TEE";
105             case KM_SECURITY_LEVEL_STRONG_BOX:
106                 return "StrongBox";
107             default:
108                 return "Unkown";
109         }
110     }
111 
getAttestationVersion()112     public int getAttestationVersion() {
113         return attestationVersion;
114     }
115 
getAttestationSecurityLevel()116     public abstract int getAttestationSecurityLevel();
117 
getRootOfTrust()118     public abstract RootOfTrust getRootOfTrust();
119 
120     // Returns one of the KM_VERSION_* values define above.
getKeymasterVersion()121     public int getKeymasterVersion() {
122         return keymasterVersion;
123     }
124 
getKeymasterSecurityLevel()125     public int getKeymasterSecurityLevel() {
126         return keymasterSecurityLevel;
127     }
128 
getAttestationChallenge()129     public byte[] getAttestationChallenge() {
130         return attestationChallenge;
131     }
132 
getUniqueId()133     public byte[] getUniqueId() {
134         return uniqueId;
135     }
136 
getSoftwareEnforced()137     public AuthorizationList getSoftwareEnforced() {
138         return softwareEnforced;
139     }
140 
getTeeEnforced()141     public AuthorizationList getTeeEnforced() {
142         return teeEnforced;
143     }
144 
getUnexpectedExtensionOids()145     public Set<String> getUnexpectedExtensionOids() {
146         return unexpectedExtensionOids;
147     }
148 
149     @Override
toString()150     public String toString() {
151         StringBuilder s = new StringBuilder();
152         s.append("Extension type: " + getClass());
153         s.append("\nAttest version: " + attestationVersion);
154         s.append("\nAttest security: " + securityLevelToString(getAttestationSecurityLevel()));
155         s.append("\nKM version: " + keymasterVersion);
156         s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel));
157 
158         s.append("\nChallenge");
159         String stringChallenge =
160                 attestationChallenge != null ? new String(attestationChallenge) : "null";
161         if (CharMatcher.ascii().matchesAllOf(stringChallenge)) {
162             s.append(": [" + stringChallenge + "]");
163         } else {
164             s.append(" (base64): [" + BaseEncoding.base64().encode(attestationChallenge) + "]");
165         }
166         if (uniqueId != null) {
167             s.append("\nUnique ID (base64): [" + BaseEncoding.base64().encode(uniqueId) + "]");
168         }
169 
170         s.append("\n-- SW enforced --");
171         s.append(softwareEnforced);
172         s.append("\n-- TEE enforced --");
173         s.append(teeEnforced);
174 
175         return s.toString();
176     }
177 
retrieveUnexpectedExtensionOids(X509Certificate x509Cert)178     Set<String> retrieveUnexpectedExtensionOids(X509Certificate x509Cert) {
179         return new ImmutableSet.Builder<String>()
180                 .addAll(
181                         x509Cert.getCriticalExtensionOIDs().stream()
182                                 .filter(s -> !KEY_USAGE_OID.equals(s))
183                                 .iterator())
184                 .addAll(
185                         x509Cert.getNonCriticalExtensionOIDs().stream()
186                                 .filter(s -> !ASN1_OID.equals(s) && !EAT_OID.equals(s))
187                                 .iterator())
188                 .build();
189     }
190 }
191