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