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