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