1 /* 2 * Copyright (C) 2022 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.android.rkpdapp.interfaces; 18 19 import android.hardware.security.keymint.DeviceInfo; 20 import android.hardware.security.keymint.IRemotelyProvisionedComponent; 21 import android.hardware.security.keymint.MacedPublicKey; 22 import android.hardware.security.keymint.ProtectedData; 23 import android.hardware.security.keymint.RpcHardwareInfo; 24 import android.os.RemoteException; 25 import android.os.ServiceSpecificException; 26 import android.util.Log; 27 28 import com.android.rkpdapp.GeekResponse; 29 import com.android.rkpdapp.RkpdException; 30 import com.android.rkpdapp.database.RkpKey; 31 import com.android.rkpdapp.metrics.ProvisioningAttempt; 32 import com.android.rkpdapp.utils.CborUtils; 33 import com.android.rkpdapp.utils.StopWatch; 34 35 import java.util.List; 36 37 import co.nstant.in.cbor.CborException; 38 import co.nstant.in.cbor.model.Array; 39 import co.nstant.in.cbor.model.ByteString; 40 import co.nstant.in.cbor.model.MajorType; 41 import co.nstant.in.cbor.model.Map; 42 43 /** 44 * Provides convenience methods for interfacing with the IRemotelyProvisionedComponent 45 * implementations. Since these APIs are internal only and subject to change, it is handy 46 * to have an abstraction layer to reduce the impact of these changes on the app. 47 */ 48 public class SystemInterface { 49 private static final String TAG = "RkpdSystemInterface"; 50 private final IRemotelyProvisionedComponent mBinder; 51 private final String mServiceName; 52 private final int mSupportedCurve; 53 SystemInterface(IRemotelyProvisionedComponent binder, String serviceName)54 public SystemInterface(IRemotelyProvisionedComponent binder, String serviceName) { 55 mServiceName = serviceName; 56 mBinder = binder; 57 try { 58 mSupportedCurve = mBinder.getHardwareInfo().supportedEekCurve; 59 } catch (RemoteException e) { 60 Log.e(TAG, "Failed to call getHardwareInfo", e); 61 throw e.rethrowAsRuntimeException(); 62 } 63 } 64 65 /** 66 * @return the fully qualified name of the underlying IRemotelyProvisionedComponent 67 */ getServiceName()68 public String getServiceName() { 69 return mServiceName; 70 } 71 72 /** 73 * @return human readable string describing this object 74 */ toString()75 public String toString() { 76 return getClass().getName() + "{" + mServiceName + "}"; 77 } 78 79 /** 80 * Generates attestation keys pair through binder service. 81 * Returns generated key in {@link RkpKey} format. 82 */ generateKey(ProvisioningAttempt metrics)83 public RkpKey generateKey(ProvisioningAttempt metrics) throws CborException, RkpdException { 84 MacedPublicKey macedPublicKey = new MacedPublicKey(); 85 try (StopWatch ignored = metrics.startBinderWait()) { 86 byte[] privKey = mBinder.generateEcdsaP256KeyPair(false, macedPublicKey); 87 return CborUtils.extractRkpKeyFromMacedKey(privKey, mServiceName, macedPublicKey); 88 } catch (RemoteException e) { 89 metrics.setStatus(ProvisioningAttempt.Status.GENERATE_KEYPAIR_FAILED); 90 Log.e(TAG, "Failed to generate key.", e); 91 throw e.rethrowAsRuntimeException(); 92 } catch (ServiceSpecificException e) { 93 metrics.setStatus(ProvisioningAttempt.Status.GENERATE_KEYPAIR_FAILED); 94 Log.e(TAG, "Failed to generate key. Failed with " + e.errorCode, e); 95 throw e; 96 } catch (CborException e) { 97 metrics.setStatus(ProvisioningAttempt.Status.GENERATE_KEYPAIR_FAILED); 98 throw e; 99 } 100 } 101 102 /** 103 * Sends a generateCsr request over the binder interface to generate the request to be sent to 104 * Remote provisioning servers. 105 * @param geekResponse Contains the challenge and GEEK chain for older implementations. Only has 106 * challenge for the newer ones. 107 * @param keysToSign array of keys to be signed. 108 */ generateCsr(ProvisioningAttempt metrics, GeekResponse geekResponse, List<RkpKey> keysToSign)109 public byte[] generateCsr(ProvisioningAttempt metrics, GeekResponse geekResponse, 110 List<RkpKey> keysToSign) throws CborException, RkpdException { 111 byte[] challenge = geekResponse.getChallenge(); 112 byte[] csrTag; 113 MacedPublicKey[] macedKeysToSign = keysToSign.stream() 114 .map(x -> { 115 MacedPublicKey key = new MacedPublicKey(); 116 key.macedKey = x.getMacedPublicKey(); 117 return key; 118 }).toArray(MacedPublicKey[]::new); 119 try (StopWatch ignored = metrics.startBinderWait()) { 120 if (getVersion() < 3) { 121 DeviceInfo deviceInfo = new DeviceInfo(); 122 ProtectedData protectedData = new ProtectedData(); 123 byte[] geekChain = geekResponse.getGeekChain(mSupportedCurve); 124 csrTag = mBinder.generateCertificateRequest(false, macedKeysToSign, geekChain, 125 challenge, deviceInfo, protectedData); 126 Array macedKeys = new Array(); 127 for (RkpKey key: keysToSign) { 128 macedKeys.add(key.getCoseKey()); 129 } 130 try { 131 Array mac0Message = new Array() 132 .add(new ByteString(CborUtils.encodeCbor( 133 CborUtils.makeProtectedHeaders()))) 134 .add(new Map()) 135 .add(new ByteString(CborUtils.encodeCbor(macedKeys))) 136 .add(new ByteString(csrTag)); 137 return CborUtils.buildCertificateRequest(deviceInfo.deviceInfo, 138 challenge, 139 protectedData.protectedData, 140 CborUtils.encodeCbor(mac0Message), 141 CborUtils.buildUnverifiedDeviceInfo()); 142 } catch (CborException | RkpdException e) { 143 Log.e(TAG, "Failed to parse/build CBOR", e); 144 metrics.setStatus(ProvisioningAttempt.Status.GENERATE_CSR_FAILED); 145 throw e; 146 } 147 } else { 148 byte[] csrBytes = mBinder.generateCertificateRequestV2(macedKeysToSign, challenge); 149 Array array = (Array) CborUtils.decodeCbor(csrBytes, "CSR request", 150 MajorType.ARRAY); 151 array.add(CborUtils.buildUnverifiedDeviceInfo()); 152 return CborUtils.encodeCbor(array); 153 } 154 } catch (RemoteException e) { 155 metrics.setStatus(ProvisioningAttempt.Status.GENERATE_CSR_FAILED); 156 Log.e(TAG, "Failed to generate CSR blob", e); 157 throw e.rethrowAsRuntimeException(); 158 } catch (ServiceSpecificException ex) { 159 metrics.setStatus(ProvisioningAttempt.Status.GENERATE_CSR_FAILED); 160 Log.e(TAG, "Failed to generate CSR blob. Failed with " + ex.errorCode, ex); 161 throw ex; 162 } catch (CborException e) { 163 metrics.setStatus(ProvisioningAttempt.Status.GENERATE_CSR_FAILED); 164 throw e; 165 } 166 } 167 168 /** 169 * Gets the implemented version for IRemotelyProvisionedComponent. 170 */ getVersion()171 public int getVersion() throws RemoteException { 172 return mBinder.getHardwareInfo().versionNumber; 173 } 174 175 /** 176 * Gets the maximum size of a batch that's supported by the underlying implementation. Since 177 * memory may be tightly constrained in the trusted runtime, the maximum batch size may be 178 * quite small. The HAL mandates an absolute minimum of 20, which is enforced via VTS. 179 */ getBatchSize()180 public int getBatchSize() throws RemoteException { 181 final int maxBatchSize = 512; 182 183 int batchSize = mBinder.getHardwareInfo().supportedNumKeysInCsr; 184 185 if (batchSize < RpcHardwareInfo.MIN_SUPPORTED_NUM_KEYS_IN_CSR) { 186 Log.w(TAG, "HAL returned a batch size that's too small (" + batchSize 187 + "), defaulting to " + RpcHardwareInfo.MIN_SUPPORTED_NUM_KEYS_IN_CSR); 188 return RpcHardwareInfo.MIN_SUPPORTED_NUM_KEYS_IN_CSR; 189 } 190 191 if (batchSize > maxBatchSize) { 192 Log.w(TAG, "HAL returned a batch size that's too large (" + batchSize 193 + "), defaulting to " + maxBatchSize); 194 return maxBatchSize; 195 } 196 197 return batchSize; 198 } 199 200 } 201