1 /*
2  * Copyright (C) 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.keystore.cts;
18 
19 import android.util.Log;
20 
21 import co.nstant.in.cbor.CborDecoder;
22 import co.nstant.in.cbor.CborException;
23 import co.nstant.in.cbor.model.DataItem;
24 import co.nstant.in.cbor.model.Map;
25 import co.nstant.in.cbor.model.Number;
26 import co.nstant.in.cbor.model.UnicodeString;
27 
28 import org.bouncycastle.asn1.ASN1Encodable;
29 
30 import java.security.cert.CertificateParsingException;
31 import java.security.cert.X509Certificate;
32 import java.util.List;
33 
34 public class EatAttestation extends Attestation {
35     static final String TAG = "EatAttestation";
36     final Map extension;
37     final RootOfTrust rootOfTrust;
38 
39     /**
40      * Constructs an {@code EatAttestation} object from the provided {@link X509Certificate},
41      * extracting the attestation data from the attestation extension.
42      *
43      * @throws CertificateParsingException if the certificate does not contain a properly-formatted
44      *     attestation extension.
45      */
EatAttestation(X509Certificate x509Cert)46     public EatAttestation(X509Certificate x509Cert)
47             throws CertificateParsingException, CborException {
48         super(x509Cert);
49         extension = getEatExtension(x509Cert);
50 
51         RootOfTrust.Builder rootOfTrustBuilder = new RootOfTrust.Builder();
52         List<Boolean> bootState = null;
53         boolean officialBuild = false;
54 
55         for (DataItem key : extension.getKeys()) {
56             int keyInt = ((Number) key).getValue().intValue();
57             switch (keyInt) {
58                 default:
59                     throw new CertificateParsingException(
60                             "Unknown EAT tag: " + key + "\n in EAT extension:\n" + toString());
61 
62                 case EatClaim.ATTESTATION_VERSION:
63                     attestationVersion = CborUtils.getInt(extension, key);
64                     break;
65                 case EatClaim.KEYMASTER_VERSION:
66                     keymasterVersion = CborUtils.getInt(extension, key);
67                     break;
68                 case EatClaim.SECURITY_LEVEL:
69                     keymasterSecurityLevel =
70                             eatSecurityLevelToKeymintSecurityLevel(
71                                     CborUtils.getInt(extension, key));
72                     break;
73                 case EatClaim.SUBMODS:
74                     Map submods = (Map) extension.get(key);
75                     softwareEnforced =
76                             new AuthorizationList(
77                                     (Map) submods.get(new UnicodeString(EatClaim.SUBMOD_SOFTWARE)));
78                     teeEnforced =
79                             new AuthorizationList(
80                                     (Map) submods.get(new UnicodeString(EatClaim.SUBMOD_TEE)));
81                     break;
82                 case EatClaim.VERIFIED_BOOT_KEY:
83                     rootOfTrustBuilder.setVerifiedBootKey(CborUtils.getBytes(extension, key));
84                     break;
85                 case EatClaim.DEVICE_LOCKED:
86                     rootOfTrustBuilder.setDeviceLocked(CborUtils.getBoolean(extension, key));
87                     break;
88                 case EatClaim.BOOT_STATE:
89                     bootState = CborUtils.getBooleanList(extension, key);
90                     break;
91                 case EatClaim.OFFICIAL_BUILD:
92                     officialBuild = CborUtils.getBoolean(extension, key);
93                     break;
94                 case EatClaim.NONCE:
95                     attestationChallenge = CborUtils.getBytes(extension, key);
96                     break;
97                 case EatClaim.CTI:
98                     Log.i(TAG, "Got CTI claim: " + CborUtils.getBytes(extension, key));
99                     uniqueId = CborUtils.getBytes(extension, key);
100                     break;
101                 case EatClaim.VERIFIED_BOOT_HASH:
102                     // TODO: ignored for now, as this is not checked in original ASN.1 tests
103                     break;
104             }
105         }
106 
107         if (bootState != null) {
108             rootOfTrustBuilder.setVerifiedBootState(
109                     eatBootStateTypeToVerifiedBootState(bootState, officialBuild));
110         }
111         rootOfTrust = rootOfTrustBuilder.build();
112     }
113 
114     /** Find the submod containing the key information, and return its security level. */
getAttestationSecurityLevel()115     public int getAttestationSecurityLevel() {
116         if (teeEnforced != null && teeEnforced.getAlgorithm() != null) {
117             return teeEnforced.getSecurityLevel();
118         } else if (softwareEnforced != null && softwareEnforced.getAlgorithm() != null) {
119             return softwareEnforced.getSecurityLevel();
120         } else {
121             return -1;
122         }
123     }
124 
getRootOfTrust()125     public RootOfTrust getRootOfTrust() {
126         return rootOfTrust;
127     }
128 
toString()129     public String toString() {
130         return super.toString() + "\nEncoded CBOR: " + extension;
131     }
132 
getEatExtension(X509Certificate x509Cert)133     Map getEatExtension(X509Certificate x509Cert)
134             throws CertificateParsingException, CborException {
135         byte[] attestationExtensionBytes = x509Cert.getExtensionValue(Attestation.EAT_OID);
136         if (attestationExtensionBytes == null || attestationExtensionBytes.length == 0) {
137             throw new CertificateParsingException("Did not find extension with OID " + EAT_OID);
138         }
139         ASN1Encodable asn1 = Asn1Utils.getAsn1EncodableFromBytes(attestationExtensionBytes);
140         byte[] cborBytes = Asn1Utils.getByteArrayFromAsn1(asn1);
141         List<DataItem> cbor = CborDecoder.decode(cborBytes);
142         return (Map) cbor.get(0);
143     }
144 
eatSecurityLevelToKeymintSecurityLevel(int eatSecurityLevel)145     static int eatSecurityLevelToKeymintSecurityLevel(int eatSecurityLevel) {
146         switch (eatSecurityLevel) {
147             case EatClaim.SECURITY_LEVEL_UNRESTRICTED:
148                 return Attestation.KM_SECURITY_LEVEL_SOFTWARE;
149             case EatClaim.SECURITY_LEVEL_SECURE_RESTRICTED:
150                 return Attestation.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
151             case EatClaim.SECURITY_LEVEL_HARDWARE:
152                 return Attestation.KM_SECURITY_LEVEL_STRONG_BOX;
153             default:
154                 throw new RuntimeException("Invalid EAT security level: " + eatSecurityLevel);
155         }
156     }
157 
eatBootStateTypeToVerifiedBootState(List<Boolean> bootState, Boolean officialBuild)158     static int eatBootStateTypeToVerifiedBootState(List<Boolean> bootState, Boolean officialBuild) {
159         if (bootState.size() != 5) {
160             throw new RuntimeException("Boot state map has unexpected size: " + bootState.size());
161         }
162         if (bootState.get(4)) {
163             throw new RuntimeException("debug-permanent-disable must never be true: " + bootState);
164         }
165         boolean verifiedOrSelfSigned = bootState.get(0);
166         if (verifiedOrSelfSigned != bootState.get(1)
167                 && verifiedOrSelfSigned != bootState.get(2)
168                 && verifiedOrSelfSigned != bootState.get(3)) {
169             throw new RuntimeException("Unexpected boot state: " + bootState);
170         }
171 
172         if (officialBuild) {
173             if (!verifiedOrSelfSigned) {
174                 throw new AssertionError("Non-verified official build");
175             }
176             return RootOfTrust.KM_VERIFIED_BOOT_VERIFIED;
177         } else {
178             return verifiedOrSelfSigned
179                     ? RootOfTrust.KM_VERIFIED_BOOT_SELF_SIGNED
180                     : RootOfTrust.KM_VERIFIED_BOOT_UNVERIFIED;
181         }
182     }
183 }
184