• 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 com.google.attestationexample;
18 
19 import com.google.common.base.CharMatcher;
20 import com.google.common.io.BaseEncoding;
21 
22 import 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     public static final int KM_SECURITY_LEVEL_STRONGBOX = 2;
45 
46     private final boolean haveAttestation;
47     private final int attestationVersion;
48     private final int attestationSecurityLevel;
49     private final int keymasterVersion;
50     private final int keymasterSecurityLevel;
51     private final byte[] attestationChallenge;
52     private final byte[] uniqueId;
53     private final AuthorizationList softwareEnforced;
54     private final AuthorizationList teeEnforced;
55 
56 
57     /**
58      * Constructs an {@code Attestation} object from the provided {@link X509Certificate},
59      * extracting the attestation data from the attestation extension.
60      *
61      * @throws CertificateParsingException if the certificate does not contain a properly-formatted
62      *                                     attestation extension.
63      */
Attestation(X509Certificate x509Cert)64     public Attestation(X509Certificate x509Cert) throws CertificateParsingException {
65         ASN1Sequence seq = getAttestationSequence(x509Cert);
66         if (seq == null) {
67             haveAttestation = false;
68             attestationVersion = 0;
69             attestationSecurityLevel = 0;
70             keymasterVersion = 0;
71             keymasterSecurityLevel = 0;
72             attestationChallenge = null;
73             uniqueId = null;
74             softwareEnforced = null;
75             teeEnforced = null;
76             return;
77         }
78 
79         haveAttestation = true;
80         attestationVersion =
81                 Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_VERSION_INDEX));
82         attestationSecurityLevel =
83                 Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
84         keymasterVersion = Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_VERSION_INDEX));
85         keymasterSecurityLevel =
86                 Asn1Utils.getIntegerFromAsn1(seq.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
87 
88         attestationChallenge =
89                 Asn1Utils.getByteArrayFromAsn1(
90                         seq.getObjectAt(Attestation.ATTESTATION_CHALLENGE_INDEX));
91 
92         uniqueId = Asn1Utils.getByteArrayFromAsn1(seq.getObjectAt(Attestation.UNIQUE_ID_INDEX));
93 
94         softwareEnforced = new AuthorizationList(seq.getObjectAt(SW_ENFORCED_INDEX));
95         teeEnforced = new AuthorizationList(seq.getObjectAt(TEE_ENFORCED_INDEX));
96     }
97 
securityLevelToString(int attestationSecurityLevel)98     public static String securityLevelToString(int attestationSecurityLevel) {
99         switch (attestationSecurityLevel) {
100             case KM_SECURITY_LEVEL_SOFTWARE:
101                 return "Software";
102             case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
103                 return "TEE";
104             case KM_SECURITY_LEVEL_STRONGBOX:
105                 return "StrongBox";
106             default:
107                 return "Unknown";
108         }
109     }
110 
getAttestationVersion()111     public int getAttestationVersion() {
112         return attestationVersion;
113     }
114 
getAttestationSecurityLevel()115     public int getAttestationSecurityLevel() {
116         return attestationSecurityLevel;
117     }
118 
getKeymasterVersion()119     public int getKeymasterVersion() {
120         return keymasterVersion;
121     }
122 
getKeymasterSecurityLevel()123     public int getKeymasterSecurityLevel() {
124         return keymasterSecurityLevel;
125     }
126 
getAttestationChallenge()127     public byte[] getAttestationChallenge() {
128         return attestationChallenge;
129     }
130 
getUniqueId()131     public byte[] getUniqueId() {
132         return uniqueId;
133     }
134 
getSoftwareEnforced()135     public AuthorizationList getSoftwareEnforced() {
136         return softwareEnforced;
137     }
138 
getTeeEnforced()139     public AuthorizationList getTeeEnforced() {
140         return teeEnforced;
141     }
142 
143     @Override
toString()144     public String toString() {
145         if (!haveAttestation) {
146             return "No attestation";
147         }
148 
149         StringBuilder s = new StringBuilder();
150         s.append("Attestation version: " + attestationVersion);
151         s.append("\nAttestation security: " + securityLevelToString(attestationSecurityLevel));
152         s.append("\nKM version: " + keymasterVersion);
153         s.append("\nKM security: " + securityLevelToString(keymasterSecurityLevel));
154 
155         s.append("\nChallenge");
156         String stringChallenge = new String(attestationChallenge);
157         if (CharMatcher.ascii().matchesAllOf(stringChallenge)) {
158             s.append(": [" + stringChallenge + "]");
159         } else {
160             s.append(" (base64): [" + BaseEncoding.base64().encode(attestationChallenge) + "]");
161         }
162         if (uniqueId != null) {
163             s.append("\nUnique ID (base64): [" + BaseEncoding.base64().encode(uniqueId) + "]");
164         }
165 
166         s.append("\n\n-- SW enforced --");
167         s.append(softwareEnforced);
168         s.append("\n\n-- TEE enforced --");
169         s.append(teeEnforced);
170         s.append("\n");
171 
172         return s.toString();
173     }
174 
getAttestationSequence(X509Certificate x509Cert)175     private ASN1Sequence getAttestationSequence(X509Certificate x509Cert)
176             throws CertificateParsingException {
177         byte[] attestationExtensionBytes = x509Cert.getExtensionValue(KEY_DESCRIPTION_OID);
178         if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
179             return null;
180         }
181         return Asn1Utils.getAsn1SequenceFromBytes(attestationExtensionBytes);
182     }
183 
184 }
185