1 /* 2 * Copyright(C) 2021 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 package com.android.javacard.keymaster; 17 18 import com.android.javacard.seprovider.KMException; 19 import com.android.javacard.seprovider.KMKey; 20 import com.android.javacard.seprovider.KMOperation; 21 import com.android.javacard.seprovider.KMSEProvider; 22 import javacard.framework.APDU; 23 import javacard.framework.ISO7816; 24 import javacard.framework.ISOException; 25 import javacard.framework.JCSystem; 26 import javacard.framework.Util; 27 28 /** 29 * This class handles remote key provisioning. Generates an RKP key and generates a certificate 30 * signing request(CSR). The generation of CSR is divided among multiple functions to the save the 31 * memory inside the Applet. The set of functions to be called sequentially in the order to complete 32 * the process of generating the CSR are processBeginSendData, processUpdateKey, 33 * processUpdateEekChain, processUpdateChallenge, processFinishSendData, and getResponse. 34 * ProcessUpdateKey is called Ntimes, where N is the number of keys. Similarly, getResponse is 35 * called multiple times till the client receives the response completely. 36 */ 37 public class KMRemotelyProvisionedComponentDevice { 38 39 // Below are the device info labels 40 // The string "brand" in hex 41 public static final byte[] BRAND = {0x62, 0x72, 0x61, 0x6E, 0x64}; 42 // The string "manufacturer" in hex 43 public static final byte[] MANUFACTURER = { 44 0x6D, 0x61, 0x6E, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65, 0x72 45 }; 46 // The string "product" in hex 47 public static final byte[] PRODUCT = {0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74}; 48 // The string "model" in hex 49 public static final byte[] MODEL = {0x6D, 0x6F, 0x64, 0x65, 0x6C}; 50 // // The string "device" in hex 51 public static final byte[] DEVICE = {0x64, 0x65, 0x76, 0x69, 0x63, 0x65}; 52 // The string "vb_state" in hex 53 public static final byte[] VB_STATE = {0x76, 0x62, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65}; 54 // The string "bootloader_state" in hex. 55 public static final byte[] BOOTLOADER_STATE = { 56 0x62, 0x6F, 0x6F, 0x74, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65 57 }; 58 // The string "vb_meta_digest" in hdex. 59 public static final byte[] VB_META_DIGEST = { 60 0X76, 0X62, 0X6D, 0X65, 0X74, 0X61, 0X5F, 0X64, 0X69, 0X67, 0X65, 0X73, 0X74 61 }; 62 // The string "os_version" in hex. 63 public static final byte[] OS_VERSION = { 64 0x6F, 0x73, 0x5F, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E 65 }; 66 // The string "system_patch_level" in hex. 67 public static final byte[] SYSTEM_PATCH_LEVEL = { 68 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 69 0x65, 0x6C 70 }; 71 // The string "boot_patch_level" in hex. 72 public static final byte[] BOOT_PATCH_LEVEL = { 73 0x62, 0x6F, 0x6F, 0x74, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C 74 }; 75 // The string "vendor_patch_level" in hex. 76 public static final byte[] VENDOR_PATCH_LEVEL = { 77 0x76, 0x65, 0x6E, 0x64, 0x6F, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 78 0x65, 0x6C 79 }; 80 // The string "version" in hex. 81 public static final byte[] DEVICE_INFO_VERSION = {0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E}; 82 // The string "security_level" in hex. 83 public static final byte[] SECURITY_LEVEL = { 84 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C 85 }; 86 // The string "fused" in hex. 87 public static final byte[] FUSED = {0x66, 0x75, 0x73, 0x65, 0x64}; 88 // Below are the Verified boot state values 89 // The string "green" in hex. 90 public static final byte[] VB_STATE_GREEN = {0x67, 0x72, 0x65, 0x65, 0x6E}; 91 // The string "yellow" in hex. 92 public static final byte[] VB_STATE_YELLOW = {0x79, 0x65, 0x6C, 0x6C, 0x6F, 0x77}; 93 // The string "orange" in hex. 94 public static final byte[] VB_STATE_ORANGE = {0x6F, 0x72, 0x61, 0x6E, 0x67, 0x65}; 95 // The string "red" in hex. 96 public static final byte[] VB_STATE_RED = {0x72, 0x65, 0x64}; 97 // Below are the boot loader state values 98 // The string "unlocked" in hex. 99 public static final byte[] UNLOCKED = {0x75, 0x6E, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64}; 100 // The string "locked" in hex. 101 public static final byte[] LOCKED = {0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64}; 102 // Device info CDDL schema version 103 public static final byte DI_SCHEMA_VERSION = 2; 104 // The string "strongbox" in hex. 105 public static final byte[] DI_SECURITY_LEVEL = { 106 0x73, 0x74, 0x72, 0x6F, 0x6E, 0x67, 0x62, 0x6F, 0x78 107 }; 108 // Represents each element size inside the data buffer. Each element has two entries 109 // 1) Length of the element and 2) offset of the element in the data buffer. 110 public static final byte DATA_INDEX_ENTRY_SIZE = 4; 111 // It is the offset, which represents the position where the element is present 112 // in the data buffer. 113 public static final byte DATA_INDEX_ENTRY_OFFSET = 2; 114 // Flag to denote TRUE 115 private static final byte TRUE = 0x01; 116 // Flag to denote FALSE 117 private static final byte FALSE = 0x00; 118 // RKP hardware info Version 119 private static final short RKP_VERSION = (short) 0x02; 120 // Below constants used to denote the type of the boot parameters. Note that these 121 // constants are only defined to be used internally. 122 private static final byte OS_VERSION_ID = 0x00; 123 private static final byte SYSTEM_PATCH_LEVEL_ID = 0x01; 124 private static final byte BOOT_PATCH_LEVEL_ID = 0x02; 125 private static final byte VENDOR_PATCH_LEVEL_ID = 0x03; 126 // Configurable flag to denote if Additional certificate chain is supported in the 127 // RKP server. 128 private static final boolean IS_ACC_SUPPORTED_IN_RKP_SERVER = false; 129 // The maximum possible output buffer. 130 private static final short MAX_SEND_DATA = 512; 131 // The string "Google Strongbox KeyMint 2" in hex. 132 private static final byte[] uniqueId = { 133 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x53, 0x74, 0x72, 0x6f, 0x6e, 0x67, 0x62, 0x6f, 0x78, 134 0x20, 0x4b, 0x65, 0x79, 0x4d, 0x69, 0x6e, 0x74, 0x20, 0x32 135 }; 136 // Flag to denote more response is available to the clients. 137 private static final byte MORE_DATA = 0x01; 138 // Flag to denote no response is available to the clients. 139 private static final byte NO_DATA = 0x00; 140 // Below are the response processing states. As the protected data response is huge it is 141 // sent back incrementally and the clients are responsible to call get response multiple times 142 // based on MORE_DATA or NO_DATA flags. BCC - Boot Certificate Chain. 143 // ACC - Additional Certificate Chain. 144 private static final byte START_PROCESSING = 0x00; 145 private static final byte PROCESSING_BCC_IN_PROGRESS = 0x02; 146 private static final byte PROCESSING_BCC_COMPLETE = 0x04; 147 private static final byte PROCESSING_ACC_IN_PROGRESS = 0x08; 148 private static final byte PROCESSING_ACC_COMPLETE = 0x0A; 149 // The data table size. 150 private static final short DATA_SIZE = 512; 151 // Number of entries in the data table. 152 private static final byte DATA_INDEX_SIZE = 11; 153 // Below are the data table offsets. 154 private static final byte EPHEMERAL_MAC_KEY = 0; 155 private static final byte TOTAL_KEYS_TO_SIGN = 1; 156 private static final byte KEYS_TO_SIGN_COUNT = 2; 157 private static final byte TEST_MODE = 3; 158 private static final byte EEK_KEY = 4; 159 private static final byte EEK_KEY_ID = 5; 160 private static final byte CHALLENGE = 6; 161 private static final byte GENERATE_CSR_PHASE = 7; 162 private static final byte EPHEMERAL_PUB_KEY = 8; 163 private static final byte RESPONSE_PROCESSING_STATE = 9; 164 private static final byte ACC_PROCESSED_LENGTH = 10; 165 166 // Below are some of the sizes defined in the data table. 167 // The size of the Ephemeral Mac key used to sign the rkp public key. 168 private static final byte EPHEMERAL_MAC_KEY_SIZE = 32; 169 // The size of short types. 170 private static final byte SHORT_SIZE = 2; 171 // The size of byte types 172 private static final byte BYTE_SIZE = 1; 173 // The size of the test mode flag. 174 private static final byte TEST_MODE_SIZE = 1; 175 // Below are the different processing stages for generateCSR. 176 // BEGIN - It is the initial stage where the process is initialized and construction of 177 // MacedPublickeys are initiated. 178 // UPDATE - Challenge, EEK and RKP keys are sent to the applet for further process. 179 // FINISH - MacedPublicKeys are constructed and construction of protected data is initiated. 180 // GET_RESPONSE - Called multiple times by the client till all the protected data is received. 181 private static final byte BEGIN = 0x01; 182 private static final byte UPDATE = 0x02; 183 private static final byte FINISH = 0x04; 184 private static final byte GET_RESPONSE = 0x06; 185 186 // RKP mac key size 187 private static final byte RKP_MAC_KEY_SIZE = 32; 188 // This holds the Google ECDSA P256 root key for the Endpoint Encryption Key. 189 public static Object[] authorizedEekRoots; 190 // Used to hold the temporary results. 191 public short[] rkpTmpVariables; 192 // Data table to hold the entries at the initial stages of generateCSR and which are used 193 // at later stages to construct the response data. 194 private byte[] data; 195 // Instance of the CBOR encoder. 196 private KMEncoder encoder; 197 // Instance of the CBOR decoder. 198 private KMDecoder decoder; 199 // Instance of the KMRepository for memory management. 200 private KMRepository repository; 201 // Instance of the provider for cyrpto operations. 202 private KMSEProvider seProvider; 203 // Instance of the KMKeymintDataStore to save or retrieve the data. 204 private KMKeymintDataStore storeDataInst; 205 // Holds the KMOperation instance. This is used to do multi part update operations. 206 private Object[] operation; 207 // Holds the current index in the data table. 208 private short[] dataIndex; 209 KMRemotelyProvisionedComponentDevice( KMEncoder encoder, KMDecoder decoder, KMRepository repository, KMSEProvider seProvider, KMKeymintDataStore storeDInst)210 public KMRemotelyProvisionedComponentDevice( 211 KMEncoder encoder, 212 KMDecoder decoder, 213 KMRepository repository, 214 KMSEProvider seProvider, 215 KMKeymintDataStore storeDInst) { 216 this.encoder = encoder; 217 this.decoder = decoder; 218 this.repository = repository; 219 this.seProvider = seProvider; 220 this.storeDataInst = storeDInst; 221 rkpTmpVariables = JCSystem.makeTransientShortArray((short) 32, JCSystem.CLEAR_ON_RESET); 222 data = JCSystem.makeTransientByteArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); 223 operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); 224 dataIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); 225 // Initialize RKP mac key 226 if (!seProvider.isUpgrading()) { 227 short offset = repository.allocReclaimableMemory((short) RKP_MAC_KEY_SIZE); 228 byte[] buffer = repository.getHeap(); 229 seProvider.getTrueRandomNumber(buffer, offset, RKP_MAC_KEY_SIZE); 230 storeDataInst.createRkpMacKey(buffer, offset, RKP_MAC_KEY_SIZE); 231 repository.reclaimMemory(RKP_MAC_KEY_SIZE); 232 } 233 operation[0] = null; 234 createAuthorizedEEKRoot(); 235 } 236 createAuthorizedEEKRoot()237 private void createAuthorizedEEKRoot() { 238 if (authorizedEekRoots == null) { 239 authorizedEekRoots = 240 new Object[] { 241 new byte[] { 242 (byte) 0x04, (byte) 0xf7, (byte) 0x14, (byte) 0x8a, (byte) 0xdb, (byte) 0x97, 243 (byte) 0xf4, (byte) 0xcc, (byte) 0x53, (byte) 0xef, (byte) 0xd2, (byte) 0x64, 244 (byte) 0x11, (byte) 0xc4, (byte) 0xe3, (byte) 0x75, (byte) 0x1f, (byte) 0x66, 245 (byte) 0x1f, (byte) 0xa4, (byte) 0x71, (byte) 0x0c, (byte) 0x6c, (byte) 0xcf, 246 (byte) 0xfa, (byte) 0x09, (byte) 0x46, (byte) 0x80, (byte) 0x74, (byte) 0x87, 247 (byte) 0x54, (byte) 0xf2, (byte) 0xad, (byte) 0x5e, (byte) 0x7f, (byte) 0x5b, 248 (byte) 0xf6, (byte) 0xec, (byte) 0xe4, (byte) 0xf6, (byte) 0x19, (byte) 0xcc, 249 (byte) 0xff, (byte) 0x13, (byte) 0x37, (byte) 0xfd, (byte) 0x0f, (byte) 0xa1, 250 (byte) 0xc8, (byte) 0x93, (byte) 0xdb, (byte) 0x18, (byte) 0x06, (byte) 0x76, 251 (byte) 0xc4, (byte) 0x5d, (byte) 0xe6, (byte) 0xd7, (byte) 0x6a, (byte) 0x77, 252 (byte) 0x86, (byte) 0xc3, (byte) 0x2d, (byte) 0xaf, (byte) 0x8f 253 }, 254 }; 255 } 256 } 257 initializeDataTable()258 private void initializeDataTable() { 259 clearDataTable(); 260 releaseOperation(); 261 dataIndex[0] = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); 262 } 263 dataAlloc(short length)264 private short dataAlloc(short length) { 265 if ((short) (dataIndex[0] + length) > (short) data.length) { 266 ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); 267 } 268 dataIndex[0] += length; 269 return (short) (dataIndex[0] - length); 270 } 271 clearDataTable()272 private void clearDataTable() { 273 Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0x00); 274 dataIndex[0] = 0x00; 275 } 276 releaseOperation()277 private void releaseOperation() { 278 if (operation[0] != null) { 279 ((KMOperation) operation[0]).abort(); 280 operation[0] = null; 281 } 282 } 283 createEntry(short index, short length)284 private short createEntry(short index, short length) { 285 index = (short) (index * DATA_INDEX_ENTRY_SIZE); 286 short ptr = dataAlloc(length); 287 Util.setShort(data, index, length); 288 Util.setShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET), ptr); 289 return ptr; 290 } 291 getEntry(short index)292 private short getEntry(short index) { 293 index = (short) (index * DATA_INDEX_ENTRY_SIZE); 294 return Util.getShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET)); 295 } 296 getEntryLength(short index)297 private short getEntryLength(short index) { 298 index = (short) (index * DATA_INDEX_ENTRY_SIZE); 299 return Util.getShort(data, index); 300 } 301 processGetRkpHwInfoCmd(APDU apdu)302 private void processGetRkpHwInfoCmd(APDU apdu) { 303 // Make the response 304 // Author name - Google. 305 short respPtr = KMArray.instance((short) 5); 306 KMArray resp = KMArray.cast(respPtr); 307 resp.add((short) 0, KMInteger.uint_16(KMError.OK)); 308 resp.add((short) 1, KMInteger.uint_16(RKP_VERSION)); 309 resp.add( 310 (short) 2, 311 KMByteBlob.instance( 312 KMKeymasterApplet.Google, (short) 0, (short) KMKeymasterApplet.Google.length)); 313 resp.add((short) 3, KMInteger.uint_8(KMType.RKP_CURVE_P256)); 314 resp.add((short) 4, KMByteBlob.instance(uniqueId, (short) 0, (short) uniqueId.length)); 315 KMKeymasterApplet.sendOutgoing(apdu, respPtr); 316 } 317 318 /** 319 * This function generates an EC key pair with attest key as purpose and creates an encrypted key 320 * blob. It then generates a COSEMac message which includes the ECDSA public key. 321 */ processGenerateRkpKey(APDU apdu)322 public void processGenerateRkpKey(APDU apdu) { 323 short arr = KMArray.instance((short) 1); 324 KMArray.cast(arr).add((short) 0, KMSimpleValue.exp()); 325 arr = KMKeymasterApplet.receiveIncoming(apdu, arr); 326 // Re-purpose the apdu buffer as scratch pad. 327 byte[] scratchPad = apdu.getBuffer(); 328 // test mode flag. 329 boolean testMode = 330 (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 0)).getValue()); 331 KMKeymasterApplet.generateRkpKey(scratchPad, getEcAttestKeyParameters()); 332 short pubKey = KMKeymasterApplet.getPubKey(); 333 short coseMac0 = constructCoseMacForRkpKey(testMode, scratchPad, pubKey); 334 // Encode the COSE_MAC0 object 335 arr = KMArray.instance((short) 3); 336 KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); 337 KMArray.cast(arr).add((short) 1, coseMac0); 338 KMArray.cast(arr).add((short) 2, KMKeymasterApplet.getPivateKey()); 339 KMKeymasterApplet.sendOutgoing(apdu, arr); 340 } 341 342 /** 343 * This is the first command of the generateCSR. 344 * Input: 345 * 1) Number of RKP keys. 346 * 2) Total length of the encoded CoseKeys (Each RKP key is represented in CoseKey) 347 * 3) Flag which represents Test mode or Production mode. 348 * Process: 349 * 1) Generate Ephemeral mac key and store in the temporary data buffer. This key is 350 * used to sign Mac_Structure, which contains array of encoded RKP keys, 351 * 2) Initialize the HMAC operation with the ephemeral mac key and do partial sign of the 352 * Mac_Structure with the input initial data received. A Multipart update on HMAC is 353 * called on each updateKey command in the second stage. 354 * 3) Store the number of RKP keys and the test mode flag in the temporary data buffer. 355 * 4) Update the phase of the generateCSR function to BEGIN. 356 * Response: 357 * Send OK response. 358 * 359 * @param apdu Input apdu 360 */ processBeginSendData(APDU apdu)361 public void processBeginSendData(APDU apdu) throws Exception { 362 try { 363 initializeDataTable(); 364 short arr = KMArray.instance((short) 3); 365 KMArray.cast(arr).add((short) 0, KMInteger.exp()); // Array length 366 KMArray.cast(arr).add((short) 1, KMInteger.exp()); // Total length of the encoded CoseKeys. 367 KMArray.cast(arr).add((short) 2, KMSimpleValue.exp()); 368 arr = KMKeymasterApplet.receiveIncoming(apdu, arr); 369 // Re-purpose the apdu buffer as scratch pad. 370 byte[] scratchPad = apdu.getBuffer(); 371 // Generate ephemeral mac key. 372 short dataEntryIndex = createEntry(EPHEMERAL_MAC_KEY, EPHEMERAL_MAC_KEY_SIZE); 373 seProvider.newRandomNumber(data, dataEntryIndex, EPHEMERAL_MAC_KEY_SIZE); 374 // Initialize hmac operation. 375 initHmacOperation(); 376 // Partially encode CoseMac structure with partial payload. 377 constructPartialPubKeysToSignMac( 378 scratchPad, 379 KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(), 380 KMInteger.cast(KMArray.cast(arr).get((short) 1)).getShort()); 381 // Store the total keys in data table. 382 dataEntryIndex = createEntry(TOTAL_KEYS_TO_SIGN, SHORT_SIZE); 383 Util.setShort( 384 data, dataEntryIndex, KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort()); 385 // Store the test mode value in data table. 386 dataEntryIndex = createEntry(TEST_MODE, TEST_MODE_SIZE); 387 data[dataEntryIndex] = 388 (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 2)).getValue()) 389 ? TRUE 390 : FALSE; 391 // Store the current csr status, which is BEGIN. 392 createEntry(GENERATE_CSR_PHASE, BYTE_SIZE); 393 updateState(BEGIN); 394 // Send response. 395 KMKeymasterApplet.sendResponse(apdu, KMError.OK); 396 } catch (Exception e) { 397 clearDataTable(); 398 releaseOperation(); 399 throw e; 400 } 401 } 402 403 /** 404 * This is the second command of the generateCSR. 405 * Input: 406 * CoseMac0 containing the RKP Key 407 * Process: 408 * 1) Validate the phase of generateCSR. Prior state should be either BEGIN or UPDATE. 409 * 2) Validate the number of RKP Keys received against the value received in first command. 410 * 3) Validate the CoseMac0 structure and extract the RKP Key. 411 * 4) Do Multipart HMAC update operation with the input as RKP key. 412 * 5) Update the number of keys received count into the data buffer. 413 * 6) Update the phase of the generateCSR function to UPDATE. 414 * Response: 415 * Send OK response. 416 * @param apdu Input apdu 417 */ processUpdateKey(APDU apdu)418 public void processUpdateKey(APDU apdu) throws Exception { 419 try { 420 // The prior state can be BEGIN or UPDATE 421 validateState((byte) (BEGIN | UPDATE)); 422 validateKeysToSignCount(); 423 short headers = KMCoseHeaders.exp(); 424 short arrInst = KMArray.instance((short) 4); 425 KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); 426 KMArray.cast(arrInst).add((short) 1, headers); 427 KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); 428 KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); 429 short arr = KMArray.exp(arrInst); 430 arr = KMKeymasterApplet.receiveIncoming(apdu, arr); 431 arrInst = KMArray.cast(arr).get((short) 0); 432 // Re-purpose the apdu buffer as scratch pad. 433 byte[] scratchPad = apdu.getBuffer(); 434 435 // Validate and extract the CoseKey from CoseMac0 message. 436 short coseKey = validateAndExtractPublicKey(arrInst, scratchPad); 437 // Encode CoseKey 438 short length = 439 KMKeymasterApplet.encodeToApduBuffer( 440 coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 441 // Do Hmac update with input as encoded CoseKey. 442 ((KMOperation) operation[0]).update(scratchPad, (short) 0, length); 443 // Increment the count each time this function gets executed. 444 // Store the count in data table. 445 short dataEntryIndex = getEntry(KEYS_TO_SIGN_COUNT); 446 if (dataEntryIndex == 0) { 447 dataEntryIndex = createEntry(KEYS_TO_SIGN_COUNT, SHORT_SIZE); 448 } 449 length = Util.getShort(data, dataEntryIndex); 450 Util.setShort(data, dataEntryIndex, ++length); 451 // Update the csr state 452 updateState(UPDATE); 453 // Send response. 454 KMKeymasterApplet.sendResponse(apdu, KMError.OK); 455 } catch (Exception e) { 456 clearDataTable(); 457 releaseOperation(); 458 throw e; 459 } 460 } 461 462 /** 463 * This is the third command of generateCSR. 464 * Input: 465 * EEK chain ordered from Root to Leaf in CoseSign1 format. 466 * Process: 467 * 1) Validate the phase of generateCSR. Prior state should be UPDATE. 468 * 2) Validate the EEK chain and extract the Leaf EEK 469 * 3) Retrieve the EEK Id and Public key of Leaf EEK and store in the data buffer. 470 * 4) Update the phase of the generateCSR function to UPDATE. 471 * Response: 472 * Send OK response. 473 * @param apdu Input apdu. 474 */ processUpdateEekChain(APDU apdu)475 public void processUpdateEekChain(APDU apdu) throws Exception { 476 try { 477 // The prior state can be BEGIN or UPDATE 478 validateState((byte) (BEGIN | UPDATE)); 479 short headers = KMCoseHeaders.exp(); 480 short arrInst = KMArray.instance((short) 4); 481 KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); 482 KMArray.cast(arrInst).add((short) 1, headers); 483 KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); 484 KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); 485 short arrSignPtr = KMArray.exp(arrInst); 486 arrInst = KMKeymasterApplet.receiveIncoming(apdu, arrSignPtr); 487 if (KMArray.cast(arrInst).length() == 0) { 488 KMException.throwIt(KMError.STATUS_INVALID_EEK); 489 } 490 // Re-purpose the apdu buffer as scratch pad. 491 byte[] scratchPad = apdu.getBuffer(); 492 // Validate eek chain. 493 short eekKey = validateAndExtractEekPub(arrInst, scratchPad); 494 // Store eek public key and eek id in the data table. 495 short eekKeyId = KMCoseKey.cast(eekKey).getKeyIdentifier(); 496 short dataEntryIndex = createEntry(EEK_KEY_ID, KMByteBlob.cast(eekKeyId).length()); 497 Util.arrayCopyNonAtomic( 498 KMByteBlob.cast(eekKeyId).getBuffer(), 499 KMByteBlob.cast(eekKeyId).getStartOff(), 500 data, 501 dataEntryIndex, 502 KMByteBlob.cast(eekKeyId).length()); 503 // Convert the coseKey to a public key. 504 short len = KMCoseKey.cast(eekKey).getEcdsa256PublicKey(scratchPad, (short) 0); 505 dataEntryIndex = createEntry(EEK_KEY, len); 506 Util.arrayCopyNonAtomic(scratchPad, (short) 0, data, dataEntryIndex, len); 507 // Update the state 508 updateState(UPDATE); 509 KMKeymasterApplet.sendResponse(apdu, KMError.OK); 510 } catch (Exception e) { 511 clearDataTable(); 512 releaseOperation(); 513 throw e; 514 } 515 } 516 517 /** 518 * This is the fourth command of generateCSR. 519 * Input: 520 * Challenge 521 * Process: 522 * 1) Validate the phase of generateCSR. Prior state should be either UPDATE or BEGIN. 523 * 2) Store the challenge in the data buffer. 524 * 3) Update the phase of the generateCSR function to UPDATE. 525 * Response: 526 * Send OK response. 527 * @param apdu Input apdu. 528 */ processUpdateChallenge(APDU apdu)529 public void processUpdateChallenge(APDU apdu) throws Exception { 530 try { 531 // The prior state can be BEGIN or UPDATE 532 validateState((byte) (BEGIN | UPDATE)); 533 short arr = KMArray.instance((short) 1); 534 KMArray.cast(arr).add((short) 0, KMByteBlob.exp()); 535 arr = KMKeymasterApplet.receiveIncoming(apdu, arr); 536 // Store the challenge in the data table. 537 short challenge = KMArray.cast(arr).get((short) 0); 538 short challengeLen = KMByteBlob.cast(challenge).length(); 539 if (challengeLen > 64) { 540 KMException.throwIt(KMError.INVALID_INPUT_LENGTH); 541 } 542 short dataEntryIndex = createEntry(CHALLENGE, challengeLen); 543 Util.arrayCopyNonAtomic( 544 KMByteBlob.cast(challenge).getBuffer(), 545 KMByteBlob.cast(challenge).getStartOff(), 546 data, 547 dataEntryIndex, 548 challengeLen); 549 // Update the state 550 updateState(UPDATE); 551 KMKeymasterApplet.sendResponse(apdu, KMError.OK); 552 } catch (Exception e) { 553 clearDataTable(); 554 releaseOperation(); 555 throw e; 556 } 557 } 558 559 /** 560 * This is the fifth command of generateCSR. 561 * Input: 562 * No input data. 563 * Process: 564 * 1) Validate the phase of generateCSR. Prior state should be UPDATE. 565 * 2) Check if all the RKP keys are received if not throw exception. 566 * 3) Finalize the HMAC operation and get the signed Mac_Structure which is called as 567 * pubKeysToSignMac. 568 * 4) Start constructing the partial protected data. Create a random 569 * nonce, initialize the AESGCM operation with the session key derived from 570 * HKDF(ECDH(EPHEMERAL_EC_KEY, EEK_KEY), KdfContext) 571 * 5) Construct Encrypt_Structure which acts as AAD for AES-GCM operation. 572 * 6) The payload for the Protected Data is [SignedMac, BCC, ACC]. Construct partial 573 * SignedMac structure. 574 * 7) Note that the HAL has to construct the CoseEncrypt structure by collecting all 575 * the pieces returned from Applet. 576 * Response: 577 * OK 578 * pubKeysToSignMac - Containes the maced RKP public keys. 579 * deviceInfo - CBOR encoded device info 580 * protectedHeader - CoseEncrypt protected header 581 * unProtectedHeader - CoseEncrypt unprotected header. 582 * ParitalCipherText - partial encrypted payload of CoseEncrypt structure. 583 * Flag to represent there is more data to retrieve. 584 * @param apdu Input apdu. 585 */ processFinishSendData(APDU apdu)586 public void processFinishSendData(APDU apdu) throws Exception { 587 try { 588 // The prior state should be UPDATE. 589 validateState(UPDATE); 590 byte[] scratchPad = apdu.getBuffer(); 591 if (data[getEntry(TOTAL_KEYS_TO_SIGN)] != data[getEntry(KEYS_TO_SIGN_COUNT)]) { 592 // Mismatch in the number of keys sent. 593 ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); 594 } 595 // PubKeysToSignMac 596 short empty = repository.alloc((short) 0); 597 short len = 598 ((KMOperation) operation[0]) 599 .sign(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0); 600 // release operation 601 releaseOperation(); 602 short pubKeysToSignMac = KMByteBlob.instance(scratchPad, (short) 0, len); 603 // Create DeviceInfo 604 short deviceInfo = createDeviceInfo(scratchPad); 605 // Generate Nonce for AES-GCM 606 seProvider.newRandomNumber(scratchPad, (short) 0, KMKeymasterApplet.AES_GCM_NONCE_LENGTH); 607 short nonce = 608 KMByteBlob.instance(scratchPad, (short) 0, KMKeymasterApplet.AES_GCM_NONCE_LENGTH); 609 // Initializes cipher instance. 610 initAesGcmOperation(scratchPad, nonce); 611 // Encode Enc_Structure as additional data for AES-GCM. 612 processAesGcmUpdateAad(scratchPad); 613 short partialPayloadLen = processSignedMac(scratchPad, pubKeysToSignMac, deviceInfo); 614 short partialCipherText = KMByteBlob.instance(scratchPad, (short) 0, partialPayloadLen); 615 short coseEncryptProtectedHeader = getCoseEncryptProtectedHeader(scratchPad); 616 short coseEncryptUnProtectedHeader = getCoseEncryptUnprotectedHeader(scratchPad, nonce); 617 len = 618 KMKeymasterApplet.encodeToApduBuffer( 619 deviceInfo, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 620 short encodedDeviceInfo = KMByteBlob.instance(scratchPad, (short) 0, len); 621 updateState(FINISH); 622 short arr = KMArray.instance((short) 7); 623 KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); 624 KMArray.cast(arr).add((short) 1, pubKeysToSignMac); 625 KMArray.cast(arr).add((short) 2, encodedDeviceInfo); 626 KMArray.cast(arr).add((short) 3, coseEncryptProtectedHeader); 627 KMArray.cast(arr).add((short) 4, coseEncryptUnProtectedHeader); 628 KMArray.cast(arr).add((short) 5, partialCipherText); 629 KMArray.cast(arr).add((short) 6, KMInteger.uint_8(MORE_DATA)); 630 KMKeymasterApplet.sendOutgoing(apdu, arr); 631 } catch (Exception e) { 632 clearDataTable(); 633 releaseOperation(); 634 throw e; 635 } 636 } 637 638 /** 639 * This is the sixth and the last command of generateCSR. This command is called multiple 640 * times by the HAL until all the cipher data and receipient structure is received. 641 * Input: 642 * No input data. 643 * Process: 644 * First the BootCertificateChain is processed: Encrypt the boot certificate chain and return 645 * the BCC. Mark the state as PROCESSING_BCC_COMPLETE. 646 * Next the AdditionalCertificateChain is processed: Incrementally encrypt ACC and send back 647 * a chunks of data. Each chunk is 512 bytes. if the processing of ACC is still in progress 648 * mark the state as PROCESSING_ACC_IN_PROGRESS otherwise mark the state as 649 * PROCESSING_ACC_COMPLETE. 650 * Finally construct and return the CoseReceipient structure. 651 * Response: 652 * OK 653 * cipher text: It can be either encrypted bcc or encrypted acc 654 * Receipient structure: This will be empty will returning the encrypted acc or bcc. 655 * Flag to represent there is more data to retrieve. 656 * @param apdu Input apdu. 657 */ processGetResponse(APDU apdu)658 public void processGetResponse(APDU apdu) throws Exception { 659 try { 660 // The prior state should be FINISH. 661 validateState((byte) (FINISH | GET_RESPONSE)); 662 byte[] scratchPad = apdu.getBuffer(); 663 short len = 0; 664 short recipientStructure = KMArray.instance((short) 0); 665 byte moreData = MORE_DATA; 666 byte state = getCurrentOutputProcessingState(); 667 switch (state) { 668 case START_PROCESSING: 669 case PROCESSING_BCC_IN_PROGRESS: 670 len = processBcc(scratchPad); 671 updateState(GET_RESPONSE); 672 break; 673 case PROCESSING_BCC_COMPLETE: 674 case PROCESSING_ACC_IN_PROGRESS: 675 len = processAdditionalCertificateChain(scratchPad); 676 updateState(GET_RESPONSE); 677 break; 678 case PROCESSING_ACC_COMPLETE: 679 recipientStructure = processRecipientStructure(scratchPad); 680 len = processFinalData(scratchPad); 681 moreData = NO_DATA; 682 releaseOperation(); 683 clearDataTable(); 684 break; 685 default: 686 KMException.throwIt(KMError.INVALID_STATE); 687 } 688 short data = KMByteBlob.instance(scratchPad, (short) 0, len); 689 short arr = KMArray.instance((short) 4); 690 KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); 691 KMArray.cast(arr).add((short) 1, data); 692 KMArray.cast(arr).add((short) 2, recipientStructure); 693 // represents there is more output to retrieve 694 KMArray.cast(arr).add((short) 3, KMInteger.uint_8(moreData)); 695 KMKeymasterApplet.sendOutgoing(apdu, arr); 696 } catch (Exception e) { 697 clearDataTable(); 698 releaseOperation(); 699 throw e; 700 } 701 } 702 process(short ins, APDU apdu)703 public void process(short ins, APDU apdu) throws Exception { 704 switch (ins) { 705 case KMKeymasterApplet.INS_GET_RKP_HARDWARE_INFO: 706 processGetRkpHwInfoCmd(apdu); 707 break; 708 case KMKeymasterApplet.INS_GENERATE_RKP_KEY_CMD: 709 processGenerateRkpKey(apdu); 710 break; 711 case KMKeymasterApplet.INS_BEGIN_SEND_DATA_CMD: 712 processBeginSendData(apdu); 713 break; 714 case KMKeymasterApplet.INS_UPDATE_KEY_CMD: 715 processUpdateKey(apdu); 716 break; 717 case KMKeymasterApplet.INS_UPDATE_EEK_CHAIN_CMD: 718 processUpdateEekChain(apdu); 719 break; 720 case KMKeymasterApplet.INS_UPDATE_CHALLENGE_CMD: 721 processUpdateChallenge(apdu); 722 break; 723 case KMKeymasterApplet.INS_FINISH_SEND_DATA_CMD: 724 processFinishSendData(apdu); 725 break; 726 case KMKeymasterApplet.INS_GET_RESPONSE_CMD: 727 processGetResponse(apdu); 728 break; 729 default: 730 ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); 731 } 732 } 733 isAdditionalCertificateChainPresent()734 private boolean isAdditionalCertificateChainPresent() { 735 if (!IS_ACC_SUPPORTED_IN_RKP_SERVER || (TRUE == data[getEntry(TEST_MODE)])) { 736 // Don't include AdditionalCertificateChain in ProtectedData if either 737 // 1. RKP server does not support processing of X.509 Additional Certificate Chain. 738 // 2. Requested CSR for test mode. 739 return false; 740 } 741 return (storeDataInst.getAdditionalCertChainLength() == 0 ? false : true); 742 } 743 processFinalData(byte[] scratchPad)744 private short processFinalData(byte[] scratchPad) { 745 // Call finish on AES GCM Cipher 746 short empty = repository.alloc((short) 0); 747 short len = 748 ((KMOperation) operation[0]) 749 .finish(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0); 750 return len; 751 } 752 getCurrentOutputProcessingState()753 private byte getCurrentOutputProcessingState() { 754 short index = getEntry(RESPONSE_PROCESSING_STATE); 755 if (index == 0) { 756 return START_PROCESSING; 757 } 758 return data[index]; 759 } 760 updateOutputProcessingState(byte state)761 private void updateOutputProcessingState(byte state) { 762 short dataEntryIndex = getEntry(RESPONSE_PROCESSING_STATE); 763 data[dataEntryIndex] = state; 764 } 765 766 /** 767 * Validates the CoseMac message and extracts the CoseKey from it. 768 * 769 * @param coseMacPtr CoseMac instance to be validated. 770 * @param scratchPad Scratch buffer used to store temp results. 771 * @return CoseKey instance. 772 */ validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad)773 private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) { 774 boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; 775 // Exp for KMCoseHeaders 776 short coseHeadersExp = KMCoseHeaders.exp(); 777 // Exp for coseky 778 short coseKeyExp = KMCoseKey.exp(); 779 780 // validate protected Headers 781 short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET); 782 ptr = 783 decoder.decode( 784 coseHeadersExp, 785 KMByteBlob.cast(ptr).getBuffer(), 786 KMByteBlob.cast(ptr).getStartOff(), 787 KMByteBlob.cast(ptr).length()); 788 789 if (!KMCoseHeaders.cast(ptr) 790 .isDataValid(rkpTmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) { 791 KMException.throwIt(KMError.STATUS_FAILED); 792 } 793 794 // Validate payload. 795 ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET); 796 ptr = 797 decoder.decode( 798 coseKeyExp, 799 KMByteBlob.cast(ptr).getBuffer(), 800 KMByteBlob.cast(ptr).getStartOff(), 801 KMByteBlob.cast(ptr).length()); 802 803 if (!KMCoseKey.cast(ptr) 804 .isDataValid( 805 rkpTmpVariables, 806 KMCose.COSE_KEY_TYPE_EC2, 807 KMType.INVALID_VALUE, 808 KMCose.COSE_ALG_ES256, 809 KMCose.COSE_ECCURVE_256)) { 810 KMException.throwIt(KMError.STATUS_FAILED); 811 } 812 813 boolean isTestKey = KMCoseKey.cast(ptr).isTestKey(); 814 if (isTestKey && !testMode) { 815 KMException.throwIt(KMError.STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); 816 } else if (!isTestKey && testMode) { 817 KMException.throwIt(KMError.STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); 818 } 819 820 // Compute CoseMac Structure and compare the macs. 821 short macStructure = 822 KMCose.constructCoseMacStructure( 823 KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET), 824 KMByteBlob.instance((short) 0), 825 KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET)); 826 short encodedLen = 827 KMKeymasterApplet.encodeToApduBuffer( 828 macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 829 830 short hmacLen = 831 rkpHmacSign(testMode, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen); 832 833 if (hmacLen 834 != KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length()) { 835 KMException.throwIt(KMError.STATUS_INVALID_MAC); 836 } 837 838 if (0 839 != Util.arrayCompare( 840 scratchPad, 841 encodedLen, 842 KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(), 843 KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)) 844 .getStartOff(), 845 hmacLen)) { 846 KMException.throwIt(KMError.STATUS_INVALID_MAC); 847 } 848 return ptr; 849 } 850 851 /** 852 * This function validates the EEK Chain and extracts the leaf public key, which is used to 853 * generate shared secret using ECDH. 854 * 855 * @param eekArr EEK cert chain array pointer. 856 * @param scratchPad Scratch buffer used to store temp results. 857 * @return CoseKey instance. 858 */ validateAndExtractEekPub(short eekArr, byte[] scratchPad)859 private short validateAndExtractEekPub(short eekArr, byte[] scratchPad) { 860 short leafPubKey = 0; 861 try { 862 leafPubKey = 863 KMKeymasterApplet.validateCertChain( 864 (TRUE == data[getEntry(TEST_MODE)]) ? false : true, // validate EEK root 865 KMCose.COSE_ALG_ES256, 866 KMCose.COSE_ALG_ECDH_ES_HKDF_256, 867 eekArr, 868 scratchPad, 869 authorizedEekRoots); 870 } catch (KMException e) { 871 KMException.throwIt(KMError.STATUS_INVALID_EEK); 872 } 873 return leafPubKey; 874 } 875 validateKeysToSignCount()876 private void validateKeysToSignCount() { 877 short index = getEntry(KEYS_TO_SIGN_COUNT); 878 short keysToSignCount = 0; 879 if (index != 0) { 880 keysToSignCount = Util.getShort(data, index); 881 } 882 if (Util.getShort(data, getEntry(TOTAL_KEYS_TO_SIGN)) <= keysToSignCount) { 883 // Mismatch in the number of keys sent. 884 ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); 885 } 886 } 887 validateState(byte expectedState)888 private void validateState(byte expectedState) { 889 short dataEntryIndex = getEntry(GENERATE_CSR_PHASE); 890 if (0 == (data[dataEntryIndex] & expectedState)) { 891 KMException.throwIt(KMError.INVALID_STATE); 892 } 893 } 894 updateState(byte state)895 private void updateState(byte state) { 896 short dataEntryIndex = getEntry(GENERATE_CSR_PHASE); 897 if (dataEntryIndex == 0) { 898 KMException.throwIt(KMError.INVALID_STATE); 899 } 900 data[dataEntryIndex] = state; 901 } 902 903 /** 904 * This function constructs a Mac Structure, encode it and signs the encoded buffer with the 905 * ephemeral mac key. 906 */ constructPartialPubKeysToSignMac( byte[] scratchPad, short arrayLength, short encodedCoseKeysLen)907 private void constructPartialPubKeysToSignMac( 908 byte[] scratchPad, short arrayLength, short encodedCoseKeysLen) { 909 short ptr; 910 short len; 911 short headerPtr = 912 KMCose.constructHeaders( 913 rkpTmpVariables, 914 KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), 915 KMType.INVALID_VALUE, 916 KMType.INVALID_VALUE, 917 KMType.INVALID_VALUE); 918 // Encode the protected header as byte blob. 919 len = 920 KMKeymasterApplet.encodeToApduBuffer( 921 headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 922 short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); 923 // create MAC_Structure 924 ptr = 925 KMCose.constructCoseMacStructure( 926 protectedHeader, KMByteBlob.instance((short) 0), KMType.INVALID_VALUE); 927 // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 928 len = 929 KMKeymasterApplet.encodeToApduBuffer( 930 ptr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 931 // Construct partial payload - Bstr Header + Array Header 932 // The maximum combined length of bstr header and array header length is 6 bytes. 933 // The lengths will never exceed Max SHORT value. 934 short arrPtr = KMArray.instance(arrayLength); 935 for (short i = 0; i < arrayLength; i++) { 936 KMArray.cast(arrPtr).add(i, KMType.INVALID_VALUE); 937 } 938 arrayLength = encoder.getEncodedLength(arrPtr); 939 short bufIndex = repository.alloc((short) 6); 940 short partialPayloadLen = 941 encoder.encodeByteBlobHeader( 942 (short) (arrayLength + encodedCoseKeysLen), repository.getHeap(), bufIndex, (short) 3); 943 944 partialPayloadLen += 945 encoder.encode( 946 arrPtr, 947 repository.getHeap(), 948 (short) (bufIndex + partialPayloadLen), 949 repository.getHeapReclaimIndex()); 950 Util.arrayCopyNonAtomic(repository.getHeap(), bufIndex, scratchPad, len, partialPayloadLen); 951 ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen)); 952 } 953 createSignedMac( KMKey deviceUniqueKeyPair, byte[] scratchPad, short deviceMapPtr, short pubKeysToSign)954 private short createSignedMac( 955 KMKey deviceUniqueKeyPair, byte[] scratchPad, short deviceMapPtr, short pubKeysToSign) { 956 // Challenge 957 short dataEntryIndex = getEntry(CHALLENGE); 958 short challengePtr = KMByteBlob.instance(data, dataEntryIndex, getEntryLength(CHALLENGE)); 959 // Ephemeral mac key 960 dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY); 961 short ephmeralMacKey = 962 KMByteBlob.instance(data, dataEntryIndex, getEntryLength(EPHEMERAL_MAC_KEY)); 963 964 /* Prepare AAD */ 965 short aad = KMArray.instance((short) 3); 966 KMArray.cast(aad).add((short) 0, challengePtr); 967 KMArray.cast(aad).add((short) 1, deviceMapPtr); 968 KMArray.cast(aad).add((short) 2, pubKeysToSign); 969 aad = 970 KMKeymasterApplet.encodeToApduBuffer( 971 aad, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 972 aad = KMByteBlob.instance(scratchPad, (short) 0, aad); 973 974 /* construct protected header */ 975 short protectedHeaders = 976 KMCose.constructHeaders( 977 rkpTmpVariables, 978 KMNInteger.uint_8(KMCose.COSE_ALG_ES256), 979 KMType.INVALID_VALUE, 980 KMType.INVALID_VALUE, 981 KMType.INVALID_VALUE); 982 protectedHeaders = 983 KMKeymasterApplet.encodeToApduBuffer( 984 protectedHeaders, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 985 protectedHeaders = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaders); 986 987 /* construct cose sign structure */ 988 short signStructure = KMCose.constructCoseSignStructure(protectedHeaders, aad, ephmeralMacKey); 989 signStructure = 990 KMKeymasterApplet.encodeToApduBuffer( 991 signStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 992 // 72 is the maximum ECDSA Signature length. 993 short maxEcdsaSignLen = 72; 994 short reclaimIndex = repository.allocReclaimableMemory(maxEcdsaSignLen); 995 short len = 996 seProvider.signWithDeviceUniqueKey( 997 deviceUniqueKeyPair, 998 scratchPad, 999 (short) 0, 1000 signStructure, 1001 repository.getHeap(), 1002 reclaimIndex); 1003 Util.arrayCopyNonAtomic(repository.getHeap(), reclaimIndex, scratchPad, (short) 0, len); 1004 repository.reclaimMemory(maxEcdsaSignLen); 1005 len = 1006 KMAsn1Parser.instance() 1007 .decodeEcdsa256Signature( 1008 KMByteBlob.instance(scratchPad, (short) 0, len), scratchPad, (short) 0); 1009 signStructure = KMByteBlob.instance(scratchPad, (short) 0, len); 1010 1011 /* Construct unprotected headers */ 1012 short unprotectedHeader = KMArray.instance((short) 0); 1013 unprotectedHeader = KMCoseHeaders.instance(unprotectedHeader); 1014 1015 /* construct Cose_Sign1 */ 1016 return KMCose.constructCoseSign1( 1017 protectedHeaders, unprotectedHeader, ephmeralMacKey, signStructure); 1018 } 1019 createDeviceUniqueKeyPair(boolean testMode, byte[] scratchPad)1020 private KMKey createDeviceUniqueKeyPair(boolean testMode, byte[] scratchPad) { 1021 KMKey deviceUniqueKeyPair; 1022 rkpTmpVariables[0] = 0; 1023 rkpTmpVariables[1] = 0; 1024 if (testMode) { 1025 seProvider.createAsymmetricKey( 1026 KMType.EC, 1027 scratchPad, 1028 (short) 0, 1029 (short) 128, 1030 scratchPad, 1031 (short) 128, 1032 (short) 128, 1033 rkpTmpVariables); 1034 deviceUniqueKeyPair = 1035 storeDataInst.createRkpTestDeviceUniqueKeyPair( 1036 scratchPad, 1037 (short) 128, 1038 rkpTmpVariables[1], 1039 scratchPad, 1040 (short) 0, 1041 rkpTmpVariables[0]); 1042 } else { 1043 deviceUniqueKeyPair = storeDataInst.getRkpDeviceUniqueKeyPair(false); 1044 } 1045 return deviceUniqueKeyPair; 1046 } 1047 1048 /** 1049 * DeviceInfo is a CBOR Map structure described by the following CDDL. 1050 * 1051 * <p>DeviceInfo = { "brand" : tstr, "manufacturer" : tstr, "product" : tstr, "model" : tstr, 1052 * "device" : tstr, "vb_state" : "green" / "yellow" / "orange", // Taken from the AVB values 1053 * "bootloader_state" : "locked" / "unlocked", // Taken from the AVB values "vbmeta_digest": bstr, 1054 * // Taken from the AVB values ? "os_version" : tstr, // Same as android.os.Build.VERSION.release 1055 * "system_patch_level" : uint, // YYYYMMDD "boot_patch_level" : uint, //YYYYMMDD 1056 * "vendor_patch_level" : uint, // YYYYMMDD "version" : 2, // TheCDDL schema version 1057 * "security_level" : "tee" / "strongbox" "fused": 1 / 0, } 1058 */ createDeviceInfo(byte[] scratchpad)1059 private short createDeviceInfo(byte[] scratchpad) { 1060 // Device Info Key Value pairs. 1061 for (short i = 0; i < 32; i++) { 1062 rkpTmpVariables[i] = KMType.INVALID_VALUE; 1063 } 1064 short dataOffset = 2; 1065 rkpTmpVariables[0] = dataOffset; 1066 rkpTmpVariables[1] = 0; 1067 short metaOffset = 0; 1068 updateItem( 1069 rkpTmpVariables, 1070 metaOffset, 1071 BRAND, 1072 getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad)); 1073 updateItem( 1074 rkpTmpVariables, 1075 metaOffset, 1076 MANUFACTURER, 1077 getAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, scratchpad)); 1078 updateItem( 1079 rkpTmpVariables, 1080 metaOffset, 1081 PRODUCT, 1082 getAttestationId(KMType.ATTESTATION_ID_PRODUCT, scratchpad)); 1083 updateItem( 1084 rkpTmpVariables, 1085 metaOffset, 1086 MODEL, 1087 getAttestationId(KMType.ATTESTATION_ID_MODEL, scratchpad)); 1088 updateItem( 1089 rkpTmpVariables, 1090 metaOffset, 1091 DEVICE, 1092 getAttestationId(KMType.ATTESTATION_ID_DEVICE, scratchpad)); 1093 updateItem(rkpTmpVariables, metaOffset, VB_STATE, getVbState()); 1094 updateItem(rkpTmpVariables, metaOffset, BOOTLOADER_STATE, getBootloaderState()); 1095 updateItem(rkpTmpVariables, metaOffset, VB_META_DIGEST, getVerifiedBootHash(scratchpad)); 1096 updateItem(rkpTmpVariables, metaOffset, OS_VERSION, getBootParams(OS_VERSION_ID, scratchpad)); 1097 updateItem( 1098 rkpTmpVariables, 1099 metaOffset, 1100 SYSTEM_PATCH_LEVEL, 1101 getBootParams(SYSTEM_PATCH_LEVEL_ID, scratchpad)); 1102 updateItem( 1103 rkpTmpVariables, 1104 metaOffset, 1105 BOOT_PATCH_LEVEL, 1106 getBootParams(BOOT_PATCH_LEVEL_ID, scratchpad)); 1107 updateItem( 1108 rkpTmpVariables, 1109 metaOffset, 1110 VENDOR_PATCH_LEVEL, 1111 getBootParams(VENDOR_PATCH_LEVEL_ID, scratchpad)); 1112 updateItem( 1113 rkpTmpVariables, metaOffset, DEVICE_INFO_VERSION, KMInteger.uint_8(DI_SCHEMA_VERSION)); 1114 updateItem( 1115 rkpTmpVariables, 1116 metaOffset, 1117 SECURITY_LEVEL, 1118 KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length)); 1119 updateItem(rkpTmpVariables, metaOffset, FUSED, KMInteger.uint_8(storeDataInst.secureBootMode)); 1120 // Create device info map. 1121 short map = KMMap.instance(rkpTmpVariables[1]); 1122 short mapIndex = 0; 1123 short index = 2; 1124 while (index < (short) 32) { 1125 if (rkpTmpVariables[index] != KMType.INVALID_VALUE) { 1126 KMMap.cast(map) 1127 .add(mapIndex++, rkpTmpVariables[index], rkpTmpVariables[(short) (index + 1)]); 1128 } 1129 index += 2; 1130 } 1131 KMMap.cast(map).canonicalize(); 1132 return map; 1133 } 1134 1135 // Below 6 methods are helper methods to create device info structure. 1136 // ---------------------------------------------------------------------------- 1137 1138 /** 1139 * Update the item inside the device info structure. 1140 * 1141 * @param deviceIds Device Info structure to be updated. 1142 * @param metaOffset Out parameter meta information. Offset 0 is index and Offset 1 is length. 1143 * @param item Key info to be updated. 1144 * @param value value to be updated. 1145 */ updateItem(short[] deviceIds, short metaOffset, byte[] item, short value)1146 private void updateItem(short[] deviceIds, short metaOffset, byte[] item, short value) { 1147 if (KMType.INVALID_VALUE != value) { 1148 deviceIds[deviceIds[metaOffset]++] = 1149 KMTextString.instance(item, (short) 0, (short) item.length); 1150 deviceIds[deviceIds[metaOffset]++] = value; 1151 deviceIds[(short) (metaOffset + 1)]++; 1152 } 1153 } 1154 getAttestationId(short attestId, byte[] scratchpad)1155 private short getAttestationId(short attestId, byte[] scratchpad) { 1156 short attIdTagLen = storeDataInst.getAttestationId(attestId, scratchpad, (short) 0); 1157 if (attIdTagLen == 0) { 1158 KMException.throwIt(KMError.INVALID_STATE); 1159 } 1160 return KMTextString.instance(scratchpad, (short) 0, attIdTagLen); 1161 } 1162 getVerifiedBootHash(byte[] scratchPad)1163 private short getVerifiedBootHash(byte[] scratchPad) { 1164 short len = storeDataInst.getVerifiedBootHash(scratchPad, (short) 0); 1165 if (len == 0) { 1166 KMException.throwIt(KMError.INVALID_STATE); 1167 } 1168 return KMByteBlob.instance(scratchPad, (short) 0, len); 1169 } 1170 getBootloaderState()1171 private short getBootloaderState() { 1172 short bootloaderState; 1173 if (storeDataInst.isDeviceBootLocked()) { 1174 bootloaderState = KMTextString.instance(LOCKED, (short) 0, (short) LOCKED.length); 1175 } else { 1176 bootloaderState = KMTextString.instance(UNLOCKED, (short) 0, (short) UNLOCKED.length); 1177 } 1178 return bootloaderState; 1179 } 1180 getVbState()1181 private short getVbState() { 1182 short state = storeDataInst.getBootState(); 1183 short vbState = KMType.INVALID_VALUE; 1184 if (state == KMType.VERIFIED_BOOT) { 1185 vbState = KMTextString.instance(VB_STATE_GREEN, (short) 0, (short) VB_STATE_GREEN.length); 1186 } else if (state == KMType.SELF_SIGNED_BOOT) { 1187 vbState = KMTextString.instance(VB_STATE_YELLOW, (short) 0, (short) VB_STATE_YELLOW.length); 1188 } else if (state == KMType.UNVERIFIED_BOOT) { 1189 vbState = KMTextString.instance(VB_STATE_ORANGE, (short) 0, (short) VB_STATE_ORANGE.length); 1190 } else if (state == KMType.FAILED_BOOT) { 1191 vbState = KMTextString.instance(VB_STATE_RED, (short) 0, (short) VB_STATE_RED.length); 1192 } 1193 return vbState; 1194 } 1195 converIntegerToTextString(short intPtr, byte[] scratchPad)1196 private short converIntegerToTextString(short intPtr, byte[] scratchPad) { 1197 // Prepare Hex Values 1198 short index = 1; 1199 scratchPad[0] = 0x30; // Ascii 0 1200 while (index < 10) { 1201 scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1); 1202 index++; 1203 } 1204 scratchPad[index++] = 0x41; // Ascii 'A' 1205 while (index < 16) { 1206 scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1); 1207 index++; 1208 } 1209 1210 short intLen = KMInteger.cast(intPtr).length(); 1211 short intOffset = KMInteger.cast(intPtr).getStartOff(); 1212 byte[] buf = repository.getHeap(); 1213 short tsPtr = KMTextString.instance((short) (intLen * 2)); 1214 short tsStartOff = KMTextString.cast(tsPtr).getStartOff(); 1215 index = 0; 1216 byte nibble; 1217 while (index < intLen) { 1218 nibble = (byte) ((byte) (buf[intOffset] >> 4) & (byte) 0x0F); 1219 buf[tsStartOff] = scratchPad[nibble]; 1220 nibble = (byte) (buf[intOffset] & 0x0F); 1221 buf[(short) (tsStartOff + 1)] = scratchPad[nibble]; 1222 index++; 1223 intOffset++; 1224 tsStartOff += 2; 1225 } 1226 return tsPtr; 1227 } 1228 getBootParams(byte bootParam, byte[] scratchPad)1229 private short getBootParams(byte bootParam, byte[] scratchPad) { 1230 short value = KMType.INVALID_VALUE; 1231 switch (bootParam) { 1232 case OS_VERSION_ID: 1233 value = storeDataInst.getOsVersion(); 1234 break; 1235 case SYSTEM_PATCH_LEVEL_ID: 1236 value = storeDataInst.getOsPatch(); 1237 break; 1238 case BOOT_PATCH_LEVEL_ID: 1239 value = storeDataInst.getBootPatchLevel(); 1240 break; 1241 case VENDOR_PATCH_LEVEL_ID: 1242 value = storeDataInst.getVendorPatchLevel(); 1243 break; 1244 default: 1245 KMException.throwIt(KMError.INVALID_ARGUMENT); 1246 } 1247 // Convert Integer to Text String for OS_VERSION. 1248 if (bootParam == OS_VERSION_ID) { 1249 value = converIntegerToTextString(value, scratchPad); 1250 } 1251 return value; 1252 } 1253 // ---------------------------------------------------------------------------- 1254 1255 // ---------------------------------------------------------------------------- 1256 // ECDH HKDF ecdhHkdfDeriveKey( byte[] privKeyA, short privKeyAOff, short privKeyALen, byte[] pubKeyA, short pubKeyAOff, short pubKeyALen, byte[] pubKeyB, short pubKeyBOff, short pubKeyBLen, byte[] scratchPad)1257 private short ecdhHkdfDeriveKey( 1258 byte[] privKeyA, 1259 short privKeyAOff, 1260 short privKeyALen, 1261 byte[] pubKeyA, 1262 short pubKeyAOff, 1263 short pubKeyALen, 1264 byte[] pubKeyB, 1265 short pubKeyBOff, 1266 short pubKeyBLen, 1267 byte[] scratchPad) { 1268 short key = 1269 seProvider.ecdhKeyAgreement( 1270 privKeyA, 1271 privKeyAOff, 1272 privKeyALen, 1273 pubKeyB, 1274 pubKeyBOff, 1275 pubKeyBLen, 1276 scratchPad, 1277 (short) 0); 1278 key = KMByteBlob.instance(scratchPad, (short) 0, key); 1279 1280 // ignore 0x04 for ephemerical public key as kdfContext should not include 0x04. 1281 pubKeyAOff += 1; 1282 pubKeyALen -= 1; 1283 pubKeyBOff += 1; 1284 pubKeyBLen -= 1; 1285 short kdfContext = 1286 KMCose.constructKdfContext( 1287 pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, true); 1288 kdfContext = 1289 KMKeymasterApplet.encodeToApduBuffer( 1290 kdfContext, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1291 kdfContext = KMByteBlob.instance(scratchPad, (short) 0, kdfContext); 1292 1293 Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 32, (byte) 0); 1294 seProvider.hkdf( 1295 KMByteBlob.cast(key).getBuffer(), 1296 KMByteBlob.cast(key).getStartOff(), 1297 KMByteBlob.cast(key).length(), 1298 scratchPad, 1299 (short) 0, 1300 (short) 32, 1301 KMByteBlob.cast(kdfContext).getBuffer(), 1302 KMByteBlob.cast(kdfContext).getStartOff(), 1303 KMByteBlob.cast(kdfContext).length(), 1304 scratchPad, 1305 (short) 32, // offset 1306 (short) 32 // Length of expected output. 1307 ); 1308 Util.arrayCopy(scratchPad, (short) 32, scratchPad, (short) 0, (short) 32); 1309 return (short) 32; 1310 } 1311 1312 // ---------------------------------------------------------------------------- 1313 // This function returns the instance of private key and It stores the public key in the 1314 // data table for later usage. generateEphemeralEcKey(byte[] scratchPad)1315 private short generateEphemeralEcKey(byte[] scratchPad) { 1316 // Generate ephemeral ec key. 1317 rkpTmpVariables[0] = 0; 1318 rkpTmpVariables[1] = 0; 1319 seProvider.createAsymmetricKey( 1320 KMType.EC, 1321 scratchPad, 1322 (short) 0, 1323 (short) 128, 1324 scratchPad, 1325 (short) 128, 1326 (short) 128, 1327 rkpTmpVariables); 1328 // Copy the ephemeral private key from scratch pad 1329 short ptr = KMByteBlob.instance(rkpTmpVariables[0]); 1330 Util.arrayCopyNonAtomic( 1331 scratchPad, 1332 (short) 0, 1333 KMByteBlob.cast(ptr).getBuffer(), 1334 KMByteBlob.cast(ptr).getStartOff(), 1335 rkpTmpVariables[0]); 1336 // Store ephemeral public key in data table for later usage. 1337 short dataEntryIndex = createEntry(EPHEMERAL_PUB_KEY, rkpTmpVariables[1]); 1338 Util.arrayCopyNonAtomic(scratchPad, (short) 128, data, dataEntryIndex, rkpTmpVariables[1]); 1339 return ptr; 1340 } 1341 initHmacOperation()1342 private void initHmacOperation() { 1343 short dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY); 1344 operation[0] = 1345 seProvider.getRkpOperation( 1346 KMType.SIGN, 1347 KMType.HMAC, 1348 KMType.SHA2_256, 1349 KMType.PADDING_NONE, 1350 (byte) 0, 1351 data, 1352 dataEntryIndex, 1353 getEntryLength(EPHEMERAL_MAC_KEY), 1354 null, 1355 (short) 0, 1356 (short) 0, 1357 (short) 0); 1358 if (operation[0] == null) { 1359 KMException.throwIt(KMError.STATUS_FAILED); 1360 } 1361 } 1362 initAesGcmOperation(byte[] scratchPad, short nonce)1363 private void initAesGcmOperation(byte[] scratchPad, short nonce) { 1364 // Generate Ephemeral mac key 1365 short privKey = generateEphemeralEcKey(scratchPad); 1366 short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); 1367 // Generate session key 1368 short eekIndex = getEntry(EEK_KEY); 1369 // Generate session key 1370 short sessionKeyLen = 1371 ecdhHkdfDeriveKey( 1372 KMByteBlob.cast(privKey).getBuffer(), /* Ephemeral Private Key */ 1373 KMByteBlob.cast(privKey).getStartOff(), 1374 KMByteBlob.cast(privKey).length(), 1375 data, /* Ephemeral Public key */ 1376 pubKeyIndex, 1377 getEntryLength(EPHEMERAL_PUB_KEY), 1378 data, /* EEK Public key */ 1379 eekIndex, 1380 getEntryLength(EEK_KEY), 1381 scratchPad /* scratchpad */); 1382 // Initialize the Cipher object. 1383 operation[0] = 1384 seProvider.getRkpOperation( 1385 KMType.ENCRYPT, 1386 KMType.AES, 1387 (byte) 0, 1388 KMType.PADDING_NONE, 1389 KMType.GCM, 1390 scratchPad, /* key */ 1391 (short) 0, 1392 sessionKeyLen, 1393 KMByteBlob.cast(nonce).getBuffer(), /* nonce */ 1394 KMByteBlob.cast(nonce).getStartOff(), 1395 KMByteBlob.cast(nonce).length(), 1396 (short) (KMKeymasterApplet.AES_GCM_AUTH_TAG_LENGTH * 8)); 1397 if (operation[0] == null) { 1398 KMException.throwIt(KMError.STATUS_FAILED); 1399 } 1400 } 1401 processRecipientStructure(byte[] scratchPad)1402 private short processRecipientStructure(byte[] scratchPad) { 1403 short protectedHeaderRecipient = 1404 KMCose.constructHeaders( 1405 rkpTmpVariables, 1406 KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256), 1407 KMType.INVALID_VALUE, 1408 KMType.INVALID_VALUE, 1409 KMType.INVALID_VALUE); 1410 // Encode the protected header as byte blob. 1411 protectedHeaderRecipient = 1412 KMKeymasterApplet.encodeToApduBuffer( 1413 protectedHeaderRecipient, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1414 protectedHeaderRecipient = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaderRecipient); 1415 1416 /* Construct unprotected headers */ 1417 short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); 1418 // prepare cosekey 1419 short coseKey = 1420 KMCose.constructCoseKey( 1421 rkpTmpVariables, 1422 KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), 1423 KMType.INVALID_VALUE, 1424 KMNInteger.uint_8(KMCose.COSE_ALG_ES256), 1425 KMInteger.uint_8(KMCose.COSE_ECCURVE_256), 1426 data, 1427 pubKeyIndex, 1428 getEntryLength(EPHEMERAL_PUB_KEY), 1429 KMType.INVALID_VALUE, 1430 false); 1431 short keyIdentifierPtr = 1432 KMByteBlob.instance(data, getEntry(EEK_KEY_ID), getEntryLength(EEK_KEY_ID)); 1433 short unprotectedHeaderRecipient = 1434 KMCose.constructHeaders( 1435 rkpTmpVariables, KMType.INVALID_VALUE, keyIdentifierPtr, KMType.INVALID_VALUE, coseKey); 1436 1437 // Construct recipients structure. 1438 return KMCose.constructRecipientsStructure( 1439 protectedHeaderRecipient, 1440 unprotectedHeaderRecipient, 1441 KMSimpleValue.instance(KMSimpleValue.NULL)); 1442 } 1443 getAdditionalCertChainProcessedLength()1444 private short getAdditionalCertChainProcessedLength() { 1445 short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH); 1446 if (dataEntryIndex == 0) { 1447 dataEntryIndex = createEntry(ACC_PROCESSED_LENGTH, SHORT_SIZE); 1448 Util.setShort(data, dataEntryIndex, (short) 0); 1449 return (short) 0; 1450 } 1451 return Util.getShort(data, dataEntryIndex); 1452 } 1453 updateAdditionalCertChainProcessedLength(short processedLen)1454 private void updateAdditionalCertChainProcessedLength(short processedLen) { 1455 short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH); 1456 Util.setShort(data, dataEntryIndex, processedLen); 1457 } 1458 processAdditionalCertificateChain(byte[] scratchPad)1459 private short processAdditionalCertificateChain(byte[] scratchPad) { 1460 byte[] persistedData = storeDataInst.getAdditionalCertChain(); 1461 short totalAccLen = Util.getShort(persistedData, (short) 0); 1462 if (totalAccLen == 0) { 1463 // No Additional certificate chain present. 1464 return 0; 1465 } 1466 short processedLen = getAdditionalCertChainProcessedLength(); 1467 short lengthToSend = (short) (totalAccLen - processedLen); 1468 if (lengthToSend > MAX_SEND_DATA) { 1469 lengthToSend = MAX_SEND_DATA; 1470 } 1471 short cipherTextLen = 1472 ((KMOperation) operation[0]) 1473 .update(persistedData, (short) (2 + processedLen), lengthToSend, scratchPad, (short) 0); 1474 processedLen += lengthToSend; 1475 updateAdditionalCertChainProcessedLength(processedLen); 1476 // Update the output processing state. 1477 updateOutputProcessingState( 1478 (processedLen == totalAccLen) ? PROCESSING_ACC_COMPLETE : PROCESSING_ACC_IN_PROGRESS); 1479 return cipherTextLen; 1480 } 1481 1482 // BCC for STRONGBOX has chain length of 2. So it can be returned in a single go. processBcc(byte[] scratchPad)1483 private short processBcc(byte[] scratchPad) { 1484 // Construct BCC 1485 boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; 1486 short len; 1487 if (testMode) { 1488 short bcc = KMKeymasterApplet.generateBcc(true, scratchPad); 1489 len = 1490 KMKeymasterApplet.encodeToApduBuffer( 1491 bcc, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1492 } else { 1493 byte[] bcc = storeDataInst.getBootCertificateChain(); 1494 len = Util.getShort(bcc, (short) 0); 1495 Util.arrayCopyNonAtomic(bcc, (short) 2, scratchPad, (short) 0, len); 1496 } 1497 short cipherTextLen = 1498 ((KMOperation) operation[0]).update(scratchPad, (short) 0, len, scratchPad, len); 1499 // move cipher text on scratch pad from starting position. 1500 Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen); 1501 createEntry(RESPONSE_PROCESSING_STATE, BYTE_SIZE); 1502 // If there is no additional certificate chain present then put the state to 1503 // PROCESSING_ACC_COMPLETE. 1504 updateOutputProcessingState( 1505 isAdditionalCertificateChainPresent() ? PROCESSING_BCC_COMPLETE : PROCESSING_ACC_COMPLETE); 1506 return cipherTextLen; 1507 } 1508 1509 // AAD is the CoseEncrypt structure processAesGcmUpdateAad(byte[] scratchPad)1510 private void processAesGcmUpdateAad(byte[] scratchPad) { 1511 short protectedHeader = 1512 KMCose.constructHeaders( 1513 rkpTmpVariables, 1514 KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), 1515 KMType.INVALID_VALUE, 1516 KMType.INVALID_VALUE, 1517 KMType.INVALID_VALUE); 1518 // Encode the protected header as byte blob. 1519 protectedHeader = 1520 KMKeymasterApplet.encodeToApduBuffer( 1521 protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1522 protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); 1523 short coseEncryptStr = 1524 KMCose.constructCoseEncryptStructure(protectedHeader, KMByteBlob.instance((short) 0)); 1525 coseEncryptStr = 1526 KMKeymasterApplet.encodeToApduBuffer( 1527 coseEncryptStr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1528 ((KMOperation) operation[0]).updateAAD(scratchPad, (short) 0, coseEncryptStr); 1529 } 1530 processSignedMac(byte[] scratchPad, short pubKeysToSignMac, short deviceInfo)1531 private short processSignedMac(byte[] scratchPad, short pubKeysToSignMac, short deviceInfo) { 1532 // Construct SignedMac 1533 KMKey deviceUniqueKeyPair = 1534 createDeviceUniqueKeyPair((TRUE == data[getEntry(TEST_MODE)]) ? true : false, scratchPad); 1535 // Create signedMac 1536 short signedMac = 1537 createSignedMac(deviceUniqueKeyPair, scratchPad, deviceInfo, pubKeysToSignMac); 1538 // Prepare partial data for encryption. 1539 short arrLength = (short) (isAdditionalCertificateChainPresent() ? 3 : 2); 1540 short arr = KMArray.instance(arrLength); 1541 KMArray.cast(arr).add((short) 0, signedMac); 1542 KMArray.cast(arr).add((short) 1, KMType.INVALID_VALUE); 1543 if (arrLength == 3) { 1544 KMArray.cast(arr).add((short) 2, KMType.INVALID_VALUE); 1545 } 1546 short len = 1547 KMKeymasterApplet.encodeToApduBuffer( 1548 arr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1549 short cipherTextLen = 1550 ((KMOperation) operation[0]).update(scratchPad, (short) 0, len, scratchPad, len); 1551 Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen); 1552 return cipherTextLen; 1553 } 1554 getCoseEncryptProtectedHeader(byte[] scratchPad)1555 private short getCoseEncryptProtectedHeader(byte[] scratchPad) { 1556 // CoseEncrypt protected headers. 1557 short protectedHeader = 1558 KMCose.constructHeaders( 1559 rkpTmpVariables, 1560 KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), 1561 KMType.INVALID_VALUE, 1562 KMType.INVALID_VALUE, 1563 KMType.INVALID_VALUE); 1564 // Encode the protected header as byte blob. 1565 protectedHeader = 1566 KMKeymasterApplet.encodeToApduBuffer( 1567 protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1568 return KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); 1569 } 1570 getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce)1571 private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) { 1572 /* CoseEncrypt unprotected headers */ 1573 return KMCose.constructHeaders( 1574 rkpTmpVariables, KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE); 1575 } 1576 constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey)1577 private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) { 1578 // prepare cosekey 1579 short coseKey = 1580 KMCose.constructCoseKey( 1581 rkpTmpVariables, 1582 KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), 1583 KMType.INVALID_VALUE, 1584 KMNInteger.uint_8(KMCose.COSE_ALG_ES256), 1585 KMInteger.uint_8(KMCose.COSE_ECCURVE_256), 1586 KMByteBlob.cast(pubKey).getBuffer(), 1587 KMByteBlob.cast(pubKey).getStartOff(), 1588 KMByteBlob.cast(pubKey).length(), 1589 KMType.INVALID_VALUE, 1590 testMode); 1591 // Encode the cose key and make it as payload. 1592 short len = 1593 KMKeymasterApplet.encodeToApduBuffer( 1594 coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1595 short payload = KMByteBlob.instance(scratchPad, (short) 0, len); 1596 // Prepare protected header, which is required to construct the COSE_MAC0 1597 short headerPtr = 1598 KMCose.constructHeaders( 1599 rkpTmpVariables, 1600 KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), 1601 KMType.INVALID_VALUE, 1602 KMType.INVALID_VALUE, 1603 KMType.INVALID_VALUE); 1604 // Encode the protected header as byte blob. 1605 len = 1606 KMKeymasterApplet.encodeToApduBuffer( 1607 headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1608 short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); 1609 // create MAC_Structure 1610 short macStructure = 1611 KMCose.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), payload); 1612 // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 1613 len = 1614 KMKeymasterApplet.encodeToApduBuffer( 1615 macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1616 // HMAC Sign. 1617 short hmacLen = rkpHmacSign(testMode, scratchPad, (short) 0, len, scratchPad, len); 1618 // Create COSE_MAC0 object 1619 short coseMac0 = 1620 KMCose.constructCoseMac0( 1621 protectedHeader, 1622 KMCoseHeaders.instance(KMArray.instance((short) 0)), 1623 payload, 1624 KMByteBlob.instance(scratchPad, len, hmacLen)); 1625 len = 1626 KMKeymasterApplet.encodeToApduBuffer( 1627 coseMac0, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); 1628 return KMByteBlob.instance(scratchPad, (short) 0, len); 1629 } 1630 getEcAttestKeyParameters()1631 private short getEcAttestKeyParameters() { 1632 short tagIndex = 0; 1633 short arrPtr = KMArray.instance((short) 6); 1634 // Key size - 256 1635 short keySize = 1636 KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); 1637 // Digest - SHA256 1638 short byteBlob = KMByteBlob.instance((short) 1); 1639 KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); 1640 short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); 1641 // Purpose - Attest 1642 byteBlob = KMByteBlob.instance((short) 1); 1643 KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY); 1644 short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); 1645 1646 KMArray.cast(arrPtr).add(tagIndex++, purpose); 1647 // Algorithm - EC 1648 KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); 1649 KMArray.cast(arrPtr).add(tagIndex++, keySize); 1650 KMArray.cast(arrPtr).add(tagIndex++, digest); 1651 // Curve - P256 1652 KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); 1653 // No Authentication is required to use this key. 1654 KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); 1655 return KMKeyParameters.instance(arrPtr); 1656 } 1657 isSignedByte(byte b)1658 private boolean isSignedByte(byte b) { 1659 return ((b & 0x0080) != 0); 1660 } 1661 writeIntegerHeader(short valueLen, byte[] data, short offset)1662 private short writeIntegerHeader(short valueLen, byte[] data, short offset) { 1663 // write length 1664 data[offset] = (byte) valueLen; 1665 // write INTEGER tag 1666 offset--; 1667 data[offset] = 0x02; 1668 return offset; 1669 } 1670 writeSequenceHeader(short valueLen, byte[] data, short offset)1671 private short writeSequenceHeader(short valueLen, byte[] data, short offset) { 1672 // write length 1673 data[offset] = (byte) valueLen; 1674 // write INTEGER tag 1675 offset--; 1676 data[offset] = 0x30; 1677 return offset; 1678 } 1679 writeSignatureData( byte[] input, short inputOff, short inputlen, byte[] output, short offset)1680 private short writeSignatureData( 1681 byte[] input, short inputOff, short inputlen, byte[] output, short offset) { 1682 Util.arrayCopyNonAtomic(input, inputOff, output, offset, inputlen); 1683 if (isSignedByte(input[inputOff])) { 1684 offset--; 1685 output[offset] = (byte) 0; 1686 } 1687 return offset; 1688 } 1689 encodeES256CoseSignSignature( byte[] input, short offset, short len, byte[] scratchPad, short scratchPadOff)1690 public short encodeES256CoseSignSignature( 1691 byte[] input, short offset, short len, byte[] scratchPad, short scratchPadOff) { 1692 // SEQ [ INTEGER(r), INTEGER(s)] 1693 // write from bottom to the top 1694 if (len != 64) { 1695 KMException.throwIt(KMError.INVALID_DATA); 1696 } 1697 short maxTotalLen = 72; 1698 short end = (short) (scratchPadOff + maxTotalLen); 1699 // write s. 1700 short start = (short) (end - 32); 1701 start = writeSignatureData(input, (short) (offset + 32), (short) 32, scratchPad, start); 1702 // write length and header 1703 short length = (short) (end - start); 1704 start--; 1705 start = writeIntegerHeader(length, scratchPad, start); 1706 // write r 1707 short rEnd = start; 1708 start = (short) (start - 32); 1709 start = writeSignatureData(input, offset, (short) 32, scratchPad, start); 1710 // write length and header 1711 length = (short) (rEnd - start); 1712 start--; 1713 start = writeIntegerHeader(length, scratchPad, start); 1714 // write length and sequence header 1715 length = (short) (end - start); 1716 start--; 1717 start = writeSequenceHeader(length, scratchPad, start); 1718 length = (short) (end - start); 1719 if (start > scratchPadOff) { 1720 // re adjust the buffer 1721 Util.arrayCopyNonAtomic(scratchPad, start, scratchPad, scratchPadOff, length); 1722 } 1723 return length; 1724 } 1725 rkpHmacSign( boolean testMode, byte[] data, short dataStart, short dataLength, byte[] signature, short signatureStart)1726 private short rkpHmacSign( 1727 boolean testMode, 1728 byte[] data, 1729 short dataStart, 1730 short dataLength, 1731 byte[] signature, 1732 short signatureStart) { 1733 short result; 1734 if (testMode) { 1735 short macKey = KMByteBlob.instance(EPHEMERAL_MAC_KEY_SIZE); 1736 Util.arrayFillNonAtomic( 1737 KMByteBlob.cast(macKey).getBuffer(), 1738 KMByteBlob.cast(macKey).getStartOff(), 1739 EPHEMERAL_MAC_KEY_SIZE, 1740 (byte) 0); 1741 result = 1742 seProvider.hmacSign( 1743 KMByteBlob.cast(macKey).getBuffer(), 1744 KMByteBlob.cast(macKey).getStartOff(), 1745 EPHEMERAL_MAC_KEY_SIZE, 1746 data, 1747 dataStart, 1748 dataLength, 1749 signature, 1750 signatureStart); 1751 } else { 1752 result = 1753 seProvider.hmacSign( 1754 storeDataInst.getRkpMacKey(), data, dataStart, dataLength, signature, signatureStart); 1755 } 1756 return result; 1757 } 1758 } 1759