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