aboutsummaryrefslogtreecommitdiff
path: root/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCose.java
blob: 2854a71a581f631730bd1334f383723e7d9a183d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
/*
 * Copyright(C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.javacard.keymaster;

import javacard.framework.ISO7816;
import javacard.framework.ISOException;

/**
 * This class constructs the Cose messages like CoseKey, CoseMac0, MacStructure, CoseSign1,
 * SignStructure, CoseEncrypt, EncryptStructure and ReceipientStructures.
 */
public class KMCose {

  // COSE SIGN1
  public static final byte COSE_SIGN1_ENTRY_COUNT = 4;
  public static final byte COSE_SIGN1_PROTECTED_PARAMS_OFFSET = 0;
  public static final byte COSE_SIGN1_PAYLOAD_OFFSET = 2;
  public static final byte COSE_SIGN1_SIGNATURE_OFFSET = 3;
  // COSE MAC0
  public static final byte COSE_MAC0_ENTRY_COUNT = 4;
  public static final byte COSE_MAC0_PROTECTED_PARAMS_OFFSET = 0;
  public static final byte COSE_MAC0_PAYLOAD_OFFSET = 2;
  public static final byte COSE_MAC0_TAG_OFFSET = 3;
  // COSE ENCRYPT
  public static final byte COSE_ENCRYPT_ENTRY_COUNT = 4;
  public static final byte COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT = 3;
  public static final byte COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT = 3;

  // COSE Labels
  public static final byte COSE_LABEL_ALGORITHM = 1;
  public static final byte COSE_LABEL_KEYID = 4;
  public static final byte COSE_LABEL_IV = 5;
  public static final byte COSE_LABEL_COSE_KEY = (byte) 0xFF; // -1

  // COSE Algorithms
  public static final byte COSE_ALG_AES_GCM_256 = 3; // AES-GCM mode w/ 256-bit key, 128-bit tag.
  public static final byte COSE_ALG_HMAC_256 = 5; // HMAC w/ SHA-256
  public static final byte COSE_ALG_ES256 = (byte) 0xF9; // ECDSA w/ SHA-256; -7
  public static final byte COSE_ALG_ECDH_ES_HKDF_256 = (byte) 0xE7; // ECDH-EC+HKDF-256; -25

  // COSE P256 EC Curve
  public static final byte COSE_ECCURVE_256 = 1;

  // COSE key types
  public static final byte COSE_KEY_TYPE_EC2 = 2;
  public static final byte COSE_KEY_TYPE_SYMMETRIC_KEY = 4;

  // COSE Key Operations
  public static final byte COSE_KEY_OP_SIGN = 1;
  public static final byte COSE_KEY_OP_VERIFY = 2;
  public static final byte COSE_KEY_OP_ENCRYPT = 3;
  public static final byte COSE_KEY_OP_DECRYPT = 4;

  // AES GCM
  public static final short AES_GCM_KEY_SIZE_BITS = 256;
  // Cose key parameters.
  public static final byte COSE_KEY_KEY_TYPE = 1;
  public static final byte COSE_KEY_KEY_ID = 2;
  public static final byte COSE_KEY_ALGORITHM = 3;
  public static final byte COSE_KEY_CURVE = -1;
  public static final byte COSE_KEY_PUBKEY_X = -2;
  public static final byte COSE_KEY_PUBKEY_Y = -3;
  public static final byte COSE_KEY_PRIV_KEY = -4;
  public static final byte[] COSE_TEST_KEY = {
    (byte) 0xFF, (byte) 0xFE, (byte) 0xEE, (byte) 0x90
  }; // -70000
  public static final byte COSE_KEY_MAX_SIZE = 4;

  // kdfcontext strings
  public static final byte[] client = {0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74};
  public static final byte[] server = {0x73, 0x65, 0x72, 0x76, 0x65, 0x72};
  // Context strings
  public static final byte[] MAC_CONTEXT = {0x4d, 0x41, 0x43, 0x30}; // MAC0
  public static final byte[] SIGNATURE1_CONTEXT = {
    0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31
  }; // Signature1
  // Certificate payload supported keys
  public static final byte ISSUER = (byte) 0x01;
  public static final byte SUBJECT = (byte) 0x02;
  public static final byte[] SUBJECT_PUBLIC_KEY = {
    (byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA8
  };
  public static final byte[] KEY_USAGE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA7};
  // text strings
  public static final byte[] TEST_ISSUER_NAME = {
    (byte) 0x49, 0x73, 0x73, 0x75, 0x65, 0x72
  }; // "Issuer"
  public static final byte[] TEST_SUBJECT_NAME = {
    0x53, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74
  }; // "Subject"
  public static final byte[] KEY_USAGE_SIGN = {0x20}; // Key usage sign

  public static final short[] COSE_KEY_LABELS = {
    KMCose.COSE_KEY_KEY_TYPE,
    KMCose.COSE_KEY_KEY_ID,
    KMCose.COSE_KEY_ALGORITHM,
    KMCose.COSE_KEY_CURVE,
    KMCose.COSE_KEY_PUBKEY_X,
    KMCose.COSE_KEY_PUBKEY_Y,
    KMCose.COSE_KEY_PRIV_KEY
  };
  public static final short[] COSE_HEADER_LABELS = {
    KMCose.COSE_LABEL_ALGORITHM,
    KMCose.COSE_LABEL_KEYID,
    KMCose.COSE_LABEL_IV,
    KMCose.COSE_LABEL_COSE_KEY
  };

  /**
   * Constructs the Cose MAC structure.
   *
   * @param protectedHeader Bstr pointer which holds the protected header.
   * @param extAad Bstr pointer which holds the external Aad.
   * @param payload Bstr pointer which holds the payload of the MAC structure.
   * @return KMArray instance of MAC structure.
   */
  public static short constructCoseMacStructure(
      short protectedHeader, short extAad, short payload) {
    // Create MAC Structure and compute HMAC as per https://tools.ietf.org/html/rfc8152#section-6.3
    //    MAC_structure = [
    //        context : "MAC" / "MAC0",
    //        protected : empty_or_serialized_map,
    //        external_aad : bstr,
    //        payload : bstr
    //   ]
    short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
    // 1 - Context
    KMArray.cast(arrPtr)
        .add(
            (short) 0,
            KMTextString.instance(
                KMCose.MAC_CONTEXT, (short) 0, (short) KMCose.MAC_CONTEXT.length));
    // 2 - Protected headers.
    KMArray.cast(arrPtr).add((short) 1, protectedHeader);
    // 3 - external aad
    KMArray.cast(arrPtr).add((short) 2, extAad);
    // 4 - payload.
    KMArray.cast(arrPtr).add((short) 3, payload);
    return arrPtr;
  }

  /**
   * Constructs the COSE_MAC0 object.
   *
   * @param protectedHeader Bstr pointer which holds the protected header.
   * @param unprotectedHeader Bstr pointer which holds the unprotected header.
   * @param payload Bstr pointer which holds the payload of the MAC structure.
   * @param tag Bstr pointer which holds the tag value.
   * @return KMArray instance of COSE_MAC0 object.
   */
  public static short constructCoseMac0(
      short protectedHeader, short unprotectedHeader, short payload, short tag) {
    // Construct Cose_MAC0
    //   COSE_Mac0 = [
    //      protectedHeader,
    //      unprotectedHeader,
    //      payload : bstr / nil,
    //      tag : bstr,
    //   ]
    short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
    // 1 - protected headers
    KMArray.cast(arrPtr).add((short) 0, protectedHeader);
    // 2 - unprotected headers
    KMArray.cast(arrPtr).add((short) 1, unprotectedHeader);
    // 2 - payload
    KMArray.cast(arrPtr).add((short) 2, payload);
    // 3 - tag
    KMArray.cast(arrPtr).add((short) 3, tag);
    // Do encode.
    return arrPtr;
  }

  /**
   * Constructs the COSE_Signature structure.
   *
   * @param protectedHeader Bstr pointer which holds the protected header.
   * @param extAad Bstr pointer which holds the aad.
   * @param payload Bstr pointer which holds the payload.
   * @return KMArray instance of COSE_Signature object.
   */
  public static short constructCoseSignStructure(
      short protectedHeader, short extAad, short payload) {
    // Sig_structure = [
    //       context : "Signature" / "Signature1" / "CounterSignature",
    //       body_protected : empty_or_serialized_map,
    //       ? sign_protected : empty_or_serialized_map,
    //       external_aad : bstr,
    //       payload : bstr
    //   ]
    short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
    // 1 - Context
    KMArray.cast(arrPtr)
        .add(
            (short) 0,
            KMTextString.instance(
                KMCose.SIGNATURE1_CONTEXT, (short) 0, (short) KMCose.SIGNATURE1_CONTEXT.length));
    // 2 - Protected headers.
    KMArray.cast(arrPtr).add((short) 1, protectedHeader);
    // 3 - external aad
    KMArray.cast(arrPtr).add((short) 2, extAad);
    // 4 - payload.
    KMArray.cast(arrPtr).add((short) 3, payload);
    return arrPtr;
  }

  /**
   * Constructs the COSE_Sign1 object.
   *
   * @param protectedHeader Bstr pointer which holds the protected header.
   * @param unProtectedHeader Bstr pointer which holds the unprotected header.
   * @param payload Bstr pointer which holds the payload.
   * @param signature Bstr pointer which holds the signature.
   * @return KMArray instance of COSE_Sign1 object.
   */
  public static short constructCoseSign1(
      short protectedHeader, short unProtectedHeader, short payload, short signature) {
    //   COSE_Sign = [
    //      protectedHeader,
    //      unprotectedHeader,
    //       payload : bstr / nil,
    //       signatures : [+ COSE_Signature]
    //   ]
    short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
    // 1 - protected headers
    KMArray.cast(arrPtr).add((short) 0, protectedHeader);
    // 2 - unprotected headers
    KMArray.cast(arrPtr).add((short) 1, unProtectedHeader);
    // 2 - payload
    KMArray.cast(arrPtr).add((short) 2, payload);
    // 3 - tag
    KMArray.cast(arrPtr).add((short) 3, signature);
    return arrPtr;
  }

  /**
   * Constructs array based on the tag values provided.
   *
   * @param tag array of tag values to be constructed.
   * @return instance of KMArray.
   */
  private static short handleCosePairTags(short[] tag, short[] keyValues, short valueIndex) {
    short index = 0;
    // var is used to calculate the length of the array.
    short var = 0;
    short tagLen = (short) tag.length;
    // var is used to calculate the length of the array.
    while (index < tagLen) {
      if (keyValues[index] != KMType.INVALID_VALUE) {
        keyValues[(short) (index + valueIndex)] =
            buildCosePairTag((byte) tag[index], keyValues[index]);
        var++;
      }
      index++;
    }
    short arrPtr = KMArray.instance(var);
    index = 0;
    // var is used to index the array.
    var = 0;
    while (index < tagLen) {
      if (keyValues[(short) (index + valueIndex)] != KMType.INVALID_VALUE) {
        KMArray.cast(arrPtr).add(var++, keyValues[(short) (index + valueIndex)]);
      }
      index++;
    }
    return arrPtr;
  }

  /**
   * Constructs the COSE_sign1 payload for certificate.
   *
   * @param issuer instance of KMCosePairTextStringTag which contains issuer value.
   * @param subject instance of KMCosePairTextStringTag which contains subject value.
   * @param subPublicKey instance of KMCosePairByteBlobTag which contains encoded KMCoseKey.
   * @param keyUsage instance of KMCosePairByteBlobTag which contains key usage value.
   * @return instance of KMArray.
   */
  public static short constructCoseCertPayload(
      short issuer, short subject, short subPublicKey, short keyUsage) {
    short certPayload = KMArray.instance((short) 4);
    KMArray.cast(certPayload).add((short) 0, issuer);
    KMArray.cast(certPayload).add((short) 1, subject);
    KMArray.cast(certPayload).add((short) 2, subPublicKey);
    KMArray.cast(certPayload).add((short) 3, keyUsage);
    certPayload = KMCoseCertPayload.instance(certPayload);
    KMCoseCertPayload.cast(certPayload).canonicalize();
    return certPayload;
  }

  /**
   * Construct headers structure. Headers can be part of COSE_Sign1, COSE_Encrypt, COSE_Mac0 and
   * COSE_Key.
   *
   * @param alg instance of either KMNInteger or KMInteger, based on the sign of algorithm value.
   * @param keyId instance of KMByteBlob which contains the key identifier.
   * @param iv instance of KMByteblob which contains the iv buffer.
   * @param ephemeralKey instance of KMCoseKey.
   * @return instance of KMCoseHeaders.
   */
  public static short constructHeaders(
      short[] buff, short alg, short keyId, short iv, short ephemeralKey) {
    buff[0] = alg;
    buff[1] = keyId;
    buff[2] = iv;
    buff[3] = ephemeralKey;
    for (short i = 4; i < 8; i++) {
      buff[i] = KMType.INVALID_VALUE;
    }
    short ptr = handleCosePairTags(COSE_HEADER_LABELS, buff, (short) 4);
    ptr = KMCoseHeaders.instance(ptr);
    KMCoseHeaders.cast(ptr).canonicalize();
    return ptr;
  }

  /**
   * Constructs the instance of KMCosePair*Tag.
   *
   * @param key value of the key.
   * @param valuePtr instance of one of KMType.
   * @return instance of KMCosePair*Value object.
   */
  public static short buildCosePairTag(byte key, short valuePtr) {
    short type = KMType.getType(valuePtr);
    short keyPtr;
    if (key < 0) {
      keyPtr = KMNInteger.uint_8(key);
    } else {
      keyPtr = KMInteger.uint_8(key);
    }
    switch (type) {
      case KMType.INTEGER_TYPE:
        return KMCosePairIntegerTag.instance(keyPtr, valuePtr);
      case KMType.NEG_INTEGER_TYPE:
        return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr);
      case KMType.BYTE_BLOB_TYPE:
        return KMCosePairByteBlobTag.instance(keyPtr, valuePtr);
      case KMType.TEXT_STRING_TYPE:
        return KMCosePairTextStringTag.instance(keyPtr, valuePtr);
      case KMType.COSE_KEY_TYPE:
        return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr);
      default:
        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
        return 0;
    }
  }

  /**
   * Constructs a CoseKey with the provided input parameters. Note that construction of the key_ops
   * label is not needed to be supported. In the KeyMint3.0 specifications: The CoseKey inside
   * MacedPublicKeys and DiceCertChain does not have key_ops label.
   *
   * @param keyType Instance of the identification of the key type.
   * @param keyId Instance of key identification value.
   * @param keyAlg Instance of the algorithm that is used with this key.
   * @param curve Instance of the EC curve that is used with this key.
   * @param pubKey Buffer containing the public key.
   * @param pubKeyOff Start offset of the buffer.
   * @param pubKeyLen Length of the public key.
   * @param privKeyPtr Instance of the private key.
   * @return Instance of the CoseKey structure.
   */
  public static short constructCoseKey(
      short[] buff,
      short keyType,
      short keyId,
      short keyAlg,
      short curve,
      byte[] pubKey,
      short pubKeyOff,
      short pubKeyLen,
      short privKeyPtr) {
    if (pubKey[pubKeyOff] == 0x04) { // uncompressed format
      pubKeyOff += 1;
      pubKeyLen -= 1;
    }
    pubKeyLen = (short) (pubKeyLen / 2);
    short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen);
    short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen);
    short coseKey = constructCoseKey(buff, keyType, keyId, keyAlg, curve, xPtr, yPtr, privKeyPtr);
    KMCoseKey.cast(coseKey).canonicalize();
    return coseKey;
  }

  /**
   * Constructs the cose key based on input parameters supplied. All the parameters must be
   * instantiated from either KMInteger or KMNInteger or KMByteblob types.
   *
   * @param keyType instance of KMInteger/KMNInteger which holds valid COSE key types.
   * @param keyId instance of KMByteBlob which holds key identifier value.
   * @param keyAlg instance of KMInteger/KMNInteger which holds valid COSE key algorithm.
   * @param curve instance of KMInteger/KMNInteger which holds valid COSE EC curve.
   * @param pubX instance of KMByteBlob which holds EC public key's x value.
   * @param pubY instance of KMByteBlob which holds EC public key's y value.
   * @param priv instance of KMByteBlob which holds EC private value.
   * @return instance of the KMCoseKey object.
   */
  public static short constructCoseKey(
      short[] buff,
      short keyType,
      short keyId,
      short keyAlg,
      short curve,
      short pubX,
      short pubY,
      short priv) {
    short valueIndex = 7;
    buff[0] = keyType;
    buff[1] = keyId;
    buff[2] = keyAlg;
    buff[3] = curve;
    buff[4] = pubX;
    buff[5] = pubY;
    buff[6] = priv;
    for (short i = valueIndex; i < 16; i++) {
      buff[i] = KMType.INVALID_VALUE;
    }
    short arrPtr = handleCosePairTags(COSE_KEY_LABELS, buff, valueIndex);
    arrPtr = KMCoseKey.instance(arrPtr);
    KMCoseKey.cast(arrPtr).canonicalize();
    return arrPtr;
  }

  /**
   * Constructs key derivation context which is required to compute HKDF.
   *
   * @param publicKeyA public key buffer from the first party.
   * @param publicKeyAOff start position of the public key buffer from first party.
   * @param publicKeyALen length of the public key buffer from first party.
   * @param publicKeyB public key buffer from the second party.
   * @param publicKeyBOff start position of the public key buffer from second party.
   * @param publicKeyBLen length of the public key buffer from second party.
   * @param senderIsA true if caller is first party, false if caller is second party.
   * @return instance of KMArray.
   */
  public static short constructKdfContext(
      byte[] publicKeyA,
      short publicKeyAOff,
      short publicKeyALen,
      byte[] publicKeyB,
      short publicKeyBOff,
      short publicKeyBLen,
      boolean senderIsA) {
    short index = 0;
    // Prepare sender info
    short senderInfo = KMArray.instance((short) 3);
    KMArray.cast(senderInfo)
        .add(index++, KMByteBlob.instance(client, (short) 0, (short) client.length));
    KMArray.cast(senderInfo).add(index++, KMByteBlob.instance((short) 0));
    KMArray.cast(senderInfo)
        .add(
            index,
            senderIsA
                ? KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen)
                : KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen));

    // Prepare recipient info
    index = 0;
    short recipientInfo = KMArray.instance((short) 3);
    KMArray.cast(recipientInfo)
        .add(index++, KMByteBlob.instance(server, (short) 0, (short) server.length));
    KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance((short) 0));
    KMArray.cast(recipientInfo)
        .add(
            index,
            senderIsA
                ? KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen)
                : KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen));

    // supply public info
    index = 0;
    short publicInfo = KMArray.instance((short) 2);
    KMArray.cast(publicInfo).add(index++, KMInteger.uint_16(AES_GCM_KEY_SIZE_BITS));
    KMArray.cast(publicInfo).add(index, KMByteBlob.instance((short) 0));

    // construct kdf context
    index = 0;
    short arrPtr = KMArray.instance((short) 4);
    KMArray.cast(arrPtr).add(index++, KMInteger.uint_8(COSE_ALG_AES_GCM_256));
    KMArray.cast(arrPtr).add(index++, senderInfo);
    KMArray.cast(arrPtr).add(index++, recipientInfo);
    KMArray.cast(arrPtr).add(index, publicInfo);

    return arrPtr;
  }
}