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.io.BaseEncoding;
21 
22 import com.android.org.bouncycastle.asn1.ASN1Sequence;
23 
24 import java.security.cert.CertificateParsingException;
25 import java.security.cert.X509Certificate;
26 
27 /**
28  * Parses an attestation certificate and provides an easy-to-use interface for examining the
29  * contents.
30  */
31 public class Attestation {
32     static final String KEY_DESCRIPTION_OID = "1.3.6.1.4.1.11129.2.1.17";
33     static final int ATTESTATION_VERSION_INDEX = 0;
34     static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
35     static final int KEYMASTER_VERSION_INDEX = 2;
36     static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
37     static final int ATTESTATION_CHALLENGE_INDEX = 4;
38     static final int UNIQUE_ID_INDEX = 5;
39     static final int SW_ENFORCED_INDEX = 6;
40     static final int TEE_ENFORCED_INDEX = 7;
41 
42     public static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
43     public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
44 
45     private final int attestationVersion;
46     private final int attestationSecurityLevel;
47     private final int keymasterVersion;
48     private final int keymasterSecurityLevel;
49     private final byte[] attestationChallenge;
50     private final byte[] uniqueId;
51     private final AuthorizationList softwareEnforced;
52     private final AuthorizationList teeEnforced;
53 
54 
55     /**
56      * Constructs an {@code Attestation} object from the provided {@link X509Certificate},
57      * extracting the attestation data from the attestation extension.
58      *
59      * @throws CertificateParsingException if the certificate does not contain a properly-formatted
60      *                                     attestation extension.
61      */
Attestation(X509Certificate x509Cert)62     public Attestation(X509Certificate x509Cert) throws CertificateParsingException {
63         ASN1Sequence seq = getAttestationSequence(x509Cert);
64 
65         attestationVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
66         attestationSecurityLevel = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
67         keymasterVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
68         keymasterSecurityLevel = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
69 
70         attestationChallenge =
71                 Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.ATTESTATION_CHALLENGE_INDEX));
72 
73         uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.UNIQUE_ID_INDEX));
74 
75         softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX));
76         teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX));
77     }
78 
securityLevelToString(int attestationSecurityLevel)79     public static String securityLevelToString(int attestationSecurityLevel) {
80         switch (attestationSecurityLevel) {
81             case KM_SECURITY_LEVEL_SOFTWARE:
82                 return "Software";
83             case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
84                 return "TEE";
85             default:
86                 return "Unkown";
87         }
88     }
89 
getAttestationVersion()90     public int getAttestationVersion() {
91         return attestationVersion;
92     }
93 
getAttestationSecurityLevel()94     public int getAttestationSecurityLevel() {
95         return attestationSecurityLevel;
96     }
97 
getKeymasterVersion()98     public int getKeymasterVersion() {
99         return keymasterVersion;
100     }
101 
getKeymasterSecurityLevel()102     public int getKeymasterSecurityLevel() {
103         return keymasterSecurityLevel;
104     }
105 
getAttestationChallenge()106     public byte[] getAttestationChallenge() {
107         return attestationChallenge;
108     }
109 
getUniqueId()110     public byte[] getUniqueId() {
111         return uniqueId;
112     }
113 
getSoftwareEnforced()114     public AuthorizationList getSoftwareEnforced() {
115         return softwareEnforced;
116     }
117 
getTeeEnforced()118     public AuthorizationList getTeeEnforced() {
119         return teeEnforced;
120     }
121 
122     @Override
toString()123     public String toString() {
124         StringBuilder s = new StringBuilder();
125         s.append("Attest version: " + attestationVersion);
126         s.append("\nAttest security: " + securityLevelToString(attestationSecurityLevel));
127         s.append("\nKM version: " + keymasterVersion);
128         s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel));
129 
130         s.append("\nChallenge");
131         String stringChallenge = new String(attestationChallenge);
132         if (CharMatcher.ASCII.matchesAllOf(stringChallenge)) {
133             s.append(": [" + stringChallenge + "]");
134         } else {
135             s.append(" (base64): [" + BaseEncoding.base64().encode(attestationChallenge) + "]");
136         }
137         if (uniqueId != null) {
138             s.append("\nUnique ID (base64): [" + BaseEncoding.base64().encode(uniqueId) + "]");
139         }
140 
141         s.append("\n-- SW enforced --");
142         s.append(softwareEnforced);
143         s.append("\n-- TEE enforced --");
144         s.append(teeEnforced);
145 
146         return s.toString();
147     }
148 
getAttestationSequence(X509Certificate x509Cert)149     private ASN1Sequence getAttestationSequence(X509Certificate x509Cert)
150             throws CertificateParsingException {
151         byte[] attestationExtensionBytes = x509Cert.getExtensionValue(KEY_DESCRIPTION_OID);
152         if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
153             throw new CertificateParsingException(
154                     "Did not find extension with OID " + KEY_DESCRIPTION_OID);
155         }
156         return Asn1Utils.getAsn1SequenceFromBytes(attestationExtensionBytes);
157     }
158 
159 }
160