1 /*
2 * draft-irtf-cfrg-hpke-07
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 */
8
9 #include "keyhi.h"
10 #include "pkcs11t.h"
11 #include "pk11func.h"
12 #include "pk11hpke.h"
13 #include "pk11pqg.h"
14 #include "secerr.h"
15 #include "secitem.h"
16 #include "secmod.h"
17 #include "secmodi.h"
18 #include "secmodti.h"
19 #include "secutil.h"
20
21 #define SERIALIZATION_VERSION 2
22
23 static const char *V1_LABEL = "HPKE-v1";
24 static const char *EXP_LABEL = "exp";
25 static const char *HPKE_LABEL = "HPKE";
26 static const char *INFO_LABEL = "info_hash";
27 static const char *KEM_LABEL = "KEM";
28 static const char *KEY_LABEL = "key";
29 static const char *NONCE_LABEL = "base_nonce";
30 static const char *PSK_ID_LABEL = "psk_id_hash";
31 static const char *SECRET_LABEL = "secret";
32 static const char *SEC_LABEL = "sec";
33 static const char *EAE_PRK_LABEL = "eae_prk";
34 static const char *SH_SEC_LABEL = "shared_secret";
35
36 struct HpkeContextStr {
37 const hpkeKemParams *kemParams;
38 const hpkeKdfParams *kdfParams;
39 const hpkeAeadParams *aeadParams;
40 PRUint8 mode; /* Base and PSK modes supported. */
41 SECItem *encapPubKey; /* Marshalled public key, sent to receiver. */
42 SECItem *baseNonce; /* Deterministic nonce for AEAD. */
43 SECItem *pskId; /* PSK identifier (non-secret). */
44 PK11Context *aeadContext; /* AEAD context used by Seal/Open. */
45 PRUint64 sequenceNumber; /* seqNo for decrypt IV construction. */
46 PK11SymKey *sharedSecret; /* ExtractAndExpand output key. */
47 PK11SymKey *key; /* Key used with the AEAD. */
48 PK11SymKey *exporterSecret; /* Derivation key for ExportSecret. */
49 PK11SymKey *psk; /* PSK imported by the application. */
50 };
51
52 static const hpkeKemParams kemParams[] = {
53 /* KEM, Nsk, Nsecret, Npk, oidTag, Hash mechanism */
54 { HpkeDhKemX25519Sha256, 32, 32, 32, SEC_OID_CURVE25519, CKM_SHA256 },
55 };
56
57 #define MAX_WRAPPED_EXP_LEN 72 // Largest kdfParams->Nh + 8
58 static const hpkeKdfParams kdfParams[] = {
59 /* KDF, Nh, mechanism */
60 { HpkeKdfHkdfSha256, SHA256_LENGTH, CKM_SHA256 },
61 { HpkeKdfHkdfSha384, SHA384_LENGTH, CKM_SHA384 },
62 { HpkeKdfHkdfSha512, SHA512_LENGTH, CKM_SHA512 },
63 };
64 #define MAX_WRAPPED_KEY_LEN 40 // Largest aeadParams->Nk + 8
65 static const hpkeAeadParams aeadParams[] = {
66 /* AEAD, Nk, Nn, tagLen, mechanism */
67 { HpkeAeadAes128Gcm, 16, 12, 16, CKM_AES_GCM },
68 { HpkeAeadAes256Gcm, 32, 12, 16, CKM_AES_GCM },
69 { HpkeAeadChaCha20Poly1305, 32, 12, 16, CKM_CHACHA20_POLY1305 },
70 };
71
72 static inline const hpkeKemParams *
kemId2Params(HpkeKemId kemId)73 kemId2Params(HpkeKemId kemId)
74 {
75 switch (kemId) {
76 case HpkeDhKemX25519Sha256:
77 return &kemParams[0];
78 default:
79 return NULL;
80 }
81 }
82
83 static inline const hpkeKdfParams *
kdfId2Params(HpkeKdfId kdfId)84 kdfId2Params(HpkeKdfId kdfId)
85 {
86 switch (kdfId) {
87 case HpkeKdfHkdfSha256:
88 return &kdfParams[0];
89 case HpkeKdfHkdfSha384:
90 return &kdfParams[1];
91 case HpkeKdfHkdfSha512:
92 return &kdfParams[2];
93 default:
94 return NULL;
95 }
96 }
97
98 static const inline hpkeAeadParams *
aeadId2Params(HpkeAeadId aeadId)99 aeadId2Params(HpkeAeadId aeadId)
100 {
101 switch (aeadId) {
102 case HpkeAeadAes128Gcm:
103 return &aeadParams[0];
104 case HpkeAeadAes256Gcm:
105 return &aeadParams[1];
106 case HpkeAeadChaCha20Poly1305:
107 return &aeadParams[2];
108 default:
109 return NULL;
110 }
111 }
112
113 static PRUint8 *
encodeNumber(PRUint64 value,PRUint8 * b,size_t count)114 encodeNumber(PRUint64 value, PRUint8 *b, size_t count)
115 {
116 PRUint64 encoded;
117 PORT_Assert(b && count > 0 && count <= sizeof(encoded));
118
119 encoded = PR_htonll(value);
120 PORT_Memcpy(b, ((unsigned char *)(&encoded)) + (sizeof(encoded) - count),
121 count);
122 return b + count;
123 }
124
125 static PRUint8 *
decodeNumber(PRUint64 * value,PRUint8 * b,size_t count)126 decodeNumber(PRUint64 *value, PRUint8 *b, size_t count)
127 {
128 unsigned int i;
129 PRUint64 number = 0;
130 PORT_Assert(b && value && count <= sizeof(*value));
131
132 for (i = 0; i < count; i++) {
133 number = (number << 8) + b[i];
134 }
135 *value = number;
136 return b + count;
137 }
138
139 SECStatus
PK11_HPKE_ValidateParameters(HpkeKemId kemId,HpkeKdfId kdfId,HpkeAeadId aeadId)140 PK11_HPKE_ValidateParameters(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId aeadId)
141 {
142 /* If more variants are added, ensure the combination is also
143 * legal. For now it is, since only the AEAD may vary. */
144 const hpkeKemParams *kem = kemId2Params(kemId);
145 const hpkeKdfParams *kdf = kdfId2Params(kdfId);
146 const hpkeAeadParams *aead = aeadId2Params(aeadId);
147 if (!kem || !kdf || !aead) {
148 PORT_SetError(SEC_ERROR_INVALID_ARGS);
149 return SECFailure;
150 }
151 return SECSuccess;
152 }
153
154 HpkeContext *
PK11_HPKE_NewContext(HpkeKemId kemId,HpkeKdfId kdfId,HpkeAeadId aeadId,PK11SymKey * psk,const SECItem * pskId)155 PK11_HPKE_NewContext(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId aeadId,
156 PK11SymKey *psk, const SECItem *pskId)
157 {
158 SECStatus rv = SECSuccess;
159 PK11SlotInfo *slot = NULL;
160 HpkeContext *cx = NULL;
161 /* Both the PSK and the PSK ID default to empty. */
162 SECItem emptyItem = { siBuffer, NULL, 0 };
163
164 cx = PORT_ZNew(HpkeContext);
165 if (!cx) {
166 return NULL;
167 }
168 cx->mode = psk ? HpkeModePsk : HpkeModeBase;
169 cx->kemParams = kemId2Params(kemId);
170 cx->kdfParams = kdfId2Params(kdfId);
171 cx->aeadParams = aeadId2Params(aeadId);
172 CHECK_FAIL_ERR((!!psk != !!pskId), SEC_ERROR_INVALID_ARGS);
173 CHECK_FAIL_ERR(!cx->kemParams || !cx->kdfParams || !cx->aeadParams,
174 SEC_ERROR_INVALID_ARGS);
175
176 /* Import the provided PSK or the default. */
177 slot = PK11_GetBestSlot(CKM_EC_KEY_PAIR_GEN, NULL);
178 CHECK_FAIL(!slot);
179 if (psk) {
180 cx->psk = PK11_ReferenceSymKey(psk);
181 cx->pskId = SECITEM_DupItem(pskId);
182 } else {
183 cx->psk = PK11_ImportDataKey(slot, CKM_HKDF_DATA, PK11_OriginUnwrap,
184 CKA_DERIVE, &emptyItem, NULL);
185 cx->pskId = SECITEM_DupItem(&emptyItem);
186 }
187 CHECK_FAIL(!cx->psk);
188 CHECK_FAIL(!cx->pskId);
189
190 CLEANUP:
191 if (rv != SECSuccess) {
192 PK11_FreeSymKey(cx->psk);
193 SECITEM_FreeItem(cx->pskId, PR_TRUE);
194 cx->pskId = NULL;
195 cx->psk = NULL;
196 PORT_Free(cx);
197 cx = NULL;
198 }
199 if (slot) {
200 PK11_FreeSlot(slot);
201 }
202 return cx;
203 }
204
205 void
PK11_HPKE_DestroyContext(HpkeContext * cx,PRBool freeit)206 PK11_HPKE_DestroyContext(HpkeContext *cx, PRBool freeit)
207 {
208 if (!cx) {
209 return;
210 }
211
212 if (cx->aeadContext) {
213 PK11_DestroyContext((PK11Context *)cx->aeadContext, PR_TRUE);
214 cx->aeadContext = NULL;
215 }
216 PK11_FreeSymKey(cx->exporterSecret);
217 PK11_FreeSymKey(cx->sharedSecret);
218 PK11_FreeSymKey(cx->key);
219 PK11_FreeSymKey(cx->psk);
220 SECITEM_FreeItem(cx->pskId, PR_TRUE);
221 SECITEM_FreeItem(cx->baseNonce, PR_TRUE);
222 SECITEM_FreeItem(cx->encapPubKey, PR_TRUE);
223 cx->exporterSecret = NULL;
224 cx->sharedSecret = NULL;
225 cx->key = NULL;
226 cx->psk = NULL;
227 cx->pskId = NULL;
228 cx->baseNonce = NULL;
229 cx->encapPubKey = NULL;
230 if (freeit) {
231 PORT_ZFree(cx, sizeof(HpkeContext));
232 }
233 }
234
235 /* Export Format:
236 struct {
237 uint8 serilizationVersion;
238 uint16 kemId;
239 uint16 kdfId;
240 uint16 aeadId;
241 uint16 modeId;
242 uint64 sequenceNumber;
243 opaque senderPubKey<1..2^16-1>;
244 opaque baseNonce<1..2^16-1>;
245 opaque key<1..2^16-1>;
246 opaque exporterSecret<1..2^16-1>;
247 } HpkeSerializedContext
248 */
249 #define EXPORTED_CTX_BASE_LEN 25 /* Fixed size plus 2B for each variable. */
250 #define REMAINING_BYTES(walker, buf) \
251 buf->len - (walker - buf->data)
252 SECStatus
PK11_HPKE_ExportContext(const HpkeContext * cx,PK11SymKey * wrapKey,SECItem ** serialized)253 PK11_HPKE_ExportContext(const HpkeContext *cx, PK11SymKey *wrapKey, SECItem **serialized)
254 {
255 SECStatus rv;
256 size_t allocLen;
257 PRUint8 *walker;
258 SECItem *keyBytes = NULL; // Maybe wrapped
259 SECItem *exporterBytes = NULL; // Maybe wrapped
260 SECItem *serializedCx = NULL;
261 PRUint8 wrappedKeyBytes[MAX_WRAPPED_KEY_LEN] = { 0 };
262 PRUint8 wrappedExpBytes[MAX_WRAPPED_EXP_LEN] = { 0 };
263 SECItem wrappedKey = { siBuffer, wrappedKeyBytes, sizeof(wrappedKeyBytes) };
264 SECItem wrappedExp = { siBuffer, wrappedExpBytes, sizeof(wrappedExpBytes) };
265
266 CHECK_FAIL_ERR((!cx || !cx->aeadContext || !serialized), SEC_ERROR_INVALID_ARGS);
267 CHECK_FAIL_ERR((cx->aeadContext->operation != (CKA_NSS_MESSAGE | CKA_DECRYPT)),
268 SEC_ERROR_NOT_A_RECIPIENT);
269
270 /* If a wrapping key was provided, do the wrap first
271 * so that we know what size to allocate. */
272 if (wrapKey) {
273 rv = PK11_WrapSymKey(CKM_AES_KEY_WRAP_KWP, NULL, wrapKey,
274 cx->key, &wrappedKey);
275 CHECK_RV(rv);
276 rv = PK11_WrapSymKey(CKM_AES_KEY_WRAP_KWP, NULL, wrapKey,
277 cx->exporterSecret, &wrappedExp);
278 CHECK_RV(rv);
279
280 keyBytes = &wrappedKey;
281 exporterBytes = &wrappedExp;
282 } else {
283 rv = PK11_ExtractKeyValue(cx->key);
284 CHECK_RV(rv);
285 keyBytes = PK11_GetKeyData(cx->key);
286 CHECK_FAIL(!keyBytes);
287 PORT_Assert(keyBytes->len == cx->aeadParams->Nk);
288
289 rv = PK11_ExtractKeyValue(cx->exporterSecret);
290 CHECK_RV(rv);
291 exporterBytes = PK11_GetKeyData(cx->exporterSecret);
292 CHECK_FAIL(!exporterBytes);
293 PORT_Assert(exporterBytes->len == cx->kdfParams->Nh);
294 }
295
296 allocLen = EXPORTED_CTX_BASE_LEN + cx->baseNonce->len + cx->encapPubKey->len;
297 allocLen += wrapKey ? wrappedKey.len : cx->aeadParams->Nk;
298 allocLen += wrapKey ? wrappedExp.len : cx->kdfParams->Nh;
299
300 serializedCx = SECITEM_AllocItem(NULL, NULL, allocLen);
301 CHECK_FAIL(!serializedCx);
302
303 walker = &serializedCx->data[0];
304 *(walker)++ = (PRUint8)SERIALIZATION_VERSION;
305
306 walker = encodeNumber(cx->kemParams->id, walker, 2);
307 walker = encodeNumber(cx->kdfParams->id, walker, 2);
308 walker = encodeNumber(cx->aeadParams->id, walker, 2);
309 walker = encodeNumber(cx->mode, walker, 2);
310 walker = encodeNumber(cx->sequenceNumber, walker, 8);
311
312 /* sender public key, serialized. */
313 walker = encodeNumber(cx->encapPubKey->len, walker, 2);
314 PORT_Memcpy(walker, cx->encapPubKey->data, cx->encapPubKey->len);
315 walker += cx->encapPubKey->len;
316
317 /* base nonce */
318 walker = encodeNumber(cx->baseNonce->len, walker, 2);
319 PORT_Memcpy(walker, cx->baseNonce->data, cx->baseNonce->len);
320 walker += cx->baseNonce->len;
321
322 /* key. */
323 walker = encodeNumber(keyBytes->len, walker, 2);
324 PORT_Memcpy(walker, keyBytes->data, keyBytes->len);
325 walker += keyBytes->len;
326
327 /* exporter_secret. */
328 walker = encodeNumber(exporterBytes->len, walker, 2);
329 PORT_Memcpy(walker, exporterBytes->data, exporterBytes->len);
330 walker += exporterBytes->len;
331
332 CHECK_FAIL_ERR(REMAINING_BYTES(walker, serializedCx) != 0,
333 SEC_ERROR_LIBRARY_FAILURE);
334 *serialized = serializedCx;
335
336 CLEANUP:
337 if (rv != SECSuccess) {
338 SECITEM_ZfreeItem(serializedCx, PR_TRUE);
339 }
340 return rv;
341 }
342
343 HpkeContext *
PK11_HPKE_ImportContext(const SECItem * serialized,PK11SymKey * wrapKey)344 PK11_HPKE_ImportContext(const SECItem *serialized, PK11SymKey *wrapKey)
345 {
346 SECStatus rv = SECSuccess;
347 HpkeContext *cx = NULL;
348 PRUint8 *walker;
349 PRUint64 tmpn;
350 PRUint8 tmp8;
351 HpkeKemId kem;
352 HpkeKdfId kdf;
353 HpkeAeadId aead;
354 PK11SlotInfo *slot = NULL;
355 PK11SymKey *tmpKey = NULL;
356 SECItem tmpItem = { siBuffer, NULL, 0 };
357 SECItem emptyItem = { siBuffer, NULL, 0 };
358
359 CHECK_FAIL_ERR((!serialized || !serialized->data || serialized->len == 0),
360 SEC_ERROR_INVALID_ARGS);
361 CHECK_FAIL_ERR((serialized->len < EXPORTED_CTX_BASE_LEN), SEC_ERROR_BAD_DATA);
362
363 walker = serialized->data;
364
365 tmp8 = *(walker++);
366 CHECK_FAIL_ERR((tmp8 != SERIALIZATION_VERSION), SEC_ERROR_BAD_DATA);
367
368 walker = decodeNumber(&tmpn, walker, 2);
369 kem = (HpkeKemId)tmpn;
370
371 walker = decodeNumber(&tmpn, walker, 2);
372 kdf = (HpkeKdfId)tmpn;
373
374 walker = decodeNumber(&tmpn, walker, 2);
375 aead = (HpkeAeadId)tmpn;
376
377 /* Create context. We'll manually set the mode, though we
378 * no longer have the PSK and have no need for it. */
379 cx = PK11_HPKE_NewContext(kem, kdf, aead, NULL, NULL);
380 CHECK_FAIL(!cx);
381
382 walker = decodeNumber(&tmpn, walker, 2);
383 CHECK_FAIL_ERR((tmpn != HpkeModeBase && tmpn != HpkeModePsk),
384 SEC_ERROR_BAD_DATA);
385 cx->mode = (HpkeModeId)tmpn;
386
387 walker = decodeNumber(&cx->sequenceNumber, walker, 8);
388 slot = PK11_GetBestSlot(CKM_HKDF_DERIVE, NULL);
389 CHECK_FAIL(!slot);
390
391 /* Import sender public key (serialized). */
392 walker = decodeNumber(&tmpn, walker, 2);
393 CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized),
394 SEC_ERROR_BAD_DATA);
395 tmpItem.data = walker;
396 tmpItem.len = tmpn;
397 cx->encapPubKey = SECITEM_DupItem(&tmpItem);
398 CHECK_FAIL(!cx->encapPubKey);
399 walker += tmpItem.len;
400
401 /* Import base_nonce. */
402 walker = decodeNumber(&tmpn, walker, 2);
403 CHECK_FAIL_ERR(tmpn != cx->aeadParams->Nn, SEC_ERROR_BAD_DATA);
404 CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized),
405 SEC_ERROR_BAD_DATA);
406 tmpItem.data = walker;
407 tmpItem.len = tmpn;
408 cx->baseNonce = SECITEM_DupItem(&tmpItem);
409 CHECK_FAIL(!cx->baseNonce);
410 walker += tmpItem.len;
411
412 /* Import key */
413 walker = decodeNumber(&tmpn, walker, 2);
414 CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized),
415 SEC_ERROR_BAD_DATA);
416 tmpItem.data = walker;
417 tmpItem.len = tmpn;
418 walker += tmpItem.len;
419 if (wrapKey) {
420 cx->key = PK11_UnwrapSymKey(wrapKey, CKM_AES_KEY_WRAP_KWP,
421 NULL, &tmpItem, cx->aeadParams->mech,
422 CKA_NSS_MESSAGE | CKA_DECRYPT, 0);
423 CHECK_FAIL(!cx->key);
424 } else {
425 CHECK_FAIL_ERR(tmpn != cx->aeadParams->Nk, SEC_ERROR_BAD_DATA);
426 tmpKey = PK11_ImportSymKey(slot, cx->aeadParams->mech,
427 PK11_OriginUnwrap, CKA_NSS_MESSAGE | CKA_DECRYPT,
428 &tmpItem, NULL);
429 CHECK_FAIL(!tmpKey);
430 cx->key = tmpKey;
431 }
432
433 /* Import exporter_secret. */
434 walker = decodeNumber(&tmpn, walker, 2);
435 CHECK_FAIL_ERR(tmpn != REMAINING_BYTES(walker, serialized),
436 SEC_ERROR_BAD_DATA);
437 tmpItem.data = walker;
438 tmpItem.len = tmpn;
439 walker += tmpItem.len;
440
441 if (wrapKey) {
442 cx->exporterSecret = PK11_UnwrapSymKey(wrapKey, CKM_AES_KEY_WRAP_KWP,
443 NULL, &tmpItem, cx->kdfParams->mech,
444 CKM_HKDF_DERIVE, 0);
445 CHECK_FAIL(!cx->exporterSecret);
446 } else {
447 CHECK_FAIL_ERR(tmpn != cx->kdfParams->Nh, SEC_ERROR_BAD_DATA);
448 tmpKey = PK11_ImportSymKey(slot, CKM_HKDF_DERIVE, PK11_OriginUnwrap,
449 CKA_DERIVE, &tmpItem, NULL);
450 CHECK_FAIL(!tmpKey);
451 cx->exporterSecret = tmpKey;
452 }
453
454 cx->aeadContext = PK11_CreateContextBySymKey(cx->aeadParams->mech,
455 CKA_NSS_MESSAGE | CKA_DECRYPT,
456 cx->key, &emptyItem);
457
458 CLEANUP:
459 if (rv != SECSuccess) {
460 PK11_FreeSymKey(tmpKey);
461 PK11_HPKE_DestroyContext(cx, PR_TRUE);
462 cx = NULL;
463 }
464 if (slot) {
465 PK11_FreeSlot(slot);
466 }
467
468 return cx;
469 }
470
471 SECStatus
PK11_HPKE_Serialize(const SECKEYPublicKey * pk,PRUint8 * buf,unsigned int * len,unsigned int maxLen)472 PK11_HPKE_Serialize(const SECKEYPublicKey *pk, PRUint8 *buf, unsigned int *len, unsigned int maxLen)
473 {
474 if (!pk || !len || pk->keyType != ecKey) {
475 PORT_SetError(SEC_ERROR_INVALID_ARGS);
476 return SECFailure;
477 }
478
479 /* If no buffer provided, return the length required for
480 * the serialized public key. */
481 if (!buf) {
482 *len = pk->u.ec.publicValue.len;
483 return SECSuccess;
484 }
485
486 if (maxLen < pk->u.ec.publicValue.len) {
487 PORT_SetError(SEC_ERROR_INPUT_LEN);
488 return SECFailure;
489 }
490
491 PORT_Memcpy(buf, pk->u.ec.publicValue.data, pk->u.ec.publicValue.len);
492 *len = pk->u.ec.publicValue.len;
493 return SECSuccess;
494 };
495
496 SECStatus
PK11_HPKE_Deserialize(const HpkeContext * cx,const PRUint8 * enc,unsigned int encLen,SECKEYPublicKey ** outPubKey)497 PK11_HPKE_Deserialize(const HpkeContext *cx, const PRUint8 *enc,
498 unsigned int encLen, SECKEYPublicKey **outPubKey)
499 {
500 SECStatus rv;
501 SECKEYPublicKey *pubKey = NULL;
502 SECOidData *oidData = NULL;
503 PLArenaPool *arena;
504
505 if (!cx || !enc || encLen == 0 || !outPubKey) {
506 PORT_SetError(SEC_ERROR_INVALID_ARGS);
507 return SECFailure;
508 }
509
510 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
511 CHECK_FAIL(!arena);
512 pubKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
513 CHECK_FAIL(!pubKey);
514
515 pubKey->arena = arena;
516 pubKey->keyType = ecKey;
517 pubKey->pkcs11Slot = NULL;
518 pubKey->pkcs11ID = CK_INVALID_HANDLE;
519
520 rv = SECITEM_MakeItem(pubKey->arena, &pubKey->u.ec.publicValue,
521 enc, encLen);
522 CHECK_RV(rv);
523 pubKey->u.ec.encoding = ECPoint_Undefined;
524 pubKey->u.ec.size = 0;
525
526 oidData = SECOID_FindOIDByTag(cx->kemParams->oidTag);
527 CHECK_FAIL_ERR(!oidData, SEC_ERROR_INVALID_ALGORITHM);
528
529 // Create parameters.
530 CHECK_FAIL(!SECITEM_AllocItem(pubKey->arena, &pubKey->u.ec.DEREncodedParams,
531 2 + oidData->oid.len));
532
533 // Set parameters.
534 pubKey->u.ec.DEREncodedParams.data[0] = SEC_ASN1_OBJECT_ID;
535 pubKey->u.ec.DEREncodedParams.data[1] = oidData->oid.len;
536 PORT_Memcpy(pubKey->u.ec.DEREncodedParams.data + 2, oidData->oid.data, oidData->oid.len);
537 *outPubKey = pubKey;
538
539 CLEANUP:
540 if (rv != SECSuccess) {
541 SECKEY_DestroyPublicKey(pubKey);
542 }
543 return rv;
544 };
545
546 static SECStatus
pk11_hpke_CheckKeys(const HpkeContext * cx,const SECKEYPublicKey * pk,const SECKEYPrivateKey * sk)547 pk11_hpke_CheckKeys(const HpkeContext *cx, const SECKEYPublicKey *pk,
548 const SECKEYPrivateKey *sk)
549 {
550 SECOidTag pkTag;
551 unsigned int i;
552 if (pk->keyType != ecKey || (sk && sk->keyType != ecKey)) {
553 PORT_SetError(SEC_ERROR_BAD_KEY);
554 return SECFailure;
555 }
556 pkTag = SECKEY_GetECCOid(&pk->u.ec.DEREncodedParams);
557 if (pkTag != cx->kemParams->oidTag) {
558 PORT_SetError(SEC_ERROR_BAD_KEY);
559 return SECFailure;
560 }
561 for (i = 0; i < PR_ARRAY_SIZE(kemParams); i++) {
562 if (cx->kemParams->oidTag == kemParams[i].oidTag) {
563 return SECSuccess;
564 }
565 }
566
567 return SECFailure;
568 }
569
570 static SECStatus
pk11_hpke_GenerateKeyPair(const HpkeContext * cx,SECKEYPublicKey ** pkE,SECKEYPrivateKey ** skE)571 pk11_hpke_GenerateKeyPair(const HpkeContext *cx, SECKEYPublicKey **pkE,
572 SECKEYPrivateKey **skE)
573 {
574 SECStatus rv = SECSuccess;
575 SECKEYPrivateKey *privKey = NULL;
576 SECKEYPublicKey *pubKey = NULL;
577 SECOidData *oidData = NULL;
578 SECKEYECParams ecp;
579 PK11SlotInfo *slot = NULL;
580 ecp.data = NULL;
581 PORT_Assert(cx && skE && pkE);
582
583 oidData = SECOID_FindOIDByTag(cx->kemParams->oidTag);
584 CHECK_FAIL_ERR(!oidData, SEC_ERROR_INVALID_ALGORITHM);
585 ecp.data = PORT_Alloc(2 + oidData->oid.len);
586 CHECK_FAIL(!ecp.data);
587
588 ecp.len = 2 + oidData->oid.len;
589 ecp.type = siDEROID;
590 ecp.data[0] = SEC_ASN1_OBJECT_ID;
591 ecp.data[1] = oidData->oid.len;
592 PORT_Memcpy(&ecp.data[2], oidData->oid.data, oidData->oid.len);
593
594 slot = PK11_GetBestSlot(CKM_EC_KEY_PAIR_GEN, NULL);
595 CHECK_FAIL(!slot);
596
597 privKey = PK11_GenerateKeyPair(slot, CKM_EC_KEY_PAIR_GEN, &ecp, &pubKey,
598 PR_FALSE, PR_TRUE, NULL);
599 CHECK_FAIL_ERR((!privKey || !pubKey), SEC_ERROR_KEYGEN_FAIL);
600 PORT_Assert(rv == SECSuccess);
601 *skE = privKey;
602 *pkE = pubKey;
603
604 CLEANUP:
605 if (rv != SECSuccess) {
606 SECKEY_DestroyPrivateKey(privKey);
607 SECKEY_DestroyPublicKey(pubKey);
608 }
609 if (slot) {
610 PK11_FreeSlot(slot);
611 }
612 PORT_Free(ecp.data);
613 return rv;
614 }
615
616 static inline SECItem *
pk11_hpke_MakeExtractLabel(const char * prefix,unsigned int prefixLen,const char * label,unsigned int labelLen,const SECItem * suiteId,const SECItem * ikm)617 pk11_hpke_MakeExtractLabel(const char *prefix, unsigned int prefixLen,
618 const char *label, unsigned int labelLen,
619 const SECItem *suiteId, const SECItem *ikm)
620 {
621 SECItem *out = NULL;
622 PRUint8 *walker;
623 out = SECITEM_AllocItem(NULL, NULL, prefixLen + labelLen + suiteId->len + (ikm ? ikm->len : 0));
624 if (!out) {
625 return NULL;
626 }
627
628 walker = out->data;
629 PORT_Memcpy(walker, prefix, prefixLen);
630 walker += prefixLen;
631 PORT_Memcpy(walker, suiteId->data, suiteId->len);
632 walker += suiteId->len;
633 PORT_Memcpy(walker, label, labelLen);
634 walker += labelLen;
635 if (ikm && ikm->data) {
636 PORT_Memcpy(walker, ikm->data, ikm->len);
637 }
638
639 return out;
640 }
641
642 static SECStatus
pk11_hpke_LabeledExtractData(const HpkeContext * cx,SECItem * salt,const SECItem * suiteId,const char * label,unsigned int labelLen,const SECItem * ikm,SECItem ** out)643 pk11_hpke_LabeledExtractData(const HpkeContext *cx, SECItem *salt,
644 const SECItem *suiteId, const char *label,
645 unsigned int labelLen, const SECItem *ikm, SECItem **out)
646 {
647 SECStatus rv;
648 CK_HKDF_PARAMS params = { 0 };
649 PK11SymKey *importedIkm = NULL;
650 PK11SymKey *prk = NULL;
651 PK11SlotInfo *slot = NULL;
652 SECItem *borrowed;
653 SECItem *outDerived = NULL;
654 SECItem *labeledIkm;
655 SECItem paramsItem = { siBuffer, (unsigned char *)¶ms,
656 sizeof(params) };
657 PORT_Assert(cx && ikm && label && labelLen && out && suiteId);
658
659 labeledIkm = pk11_hpke_MakeExtractLabel(V1_LABEL, strlen(V1_LABEL), label, labelLen, suiteId, ikm);
660 CHECK_FAIL(!labeledIkm);
661 params.bExtract = CK_TRUE;
662 params.bExpand = CK_FALSE;
663 params.prfHashMechanism = cx->kdfParams->mech;
664 params.ulSaltType = salt ? CKF_HKDF_SALT_DATA : CKF_HKDF_SALT_NULL;
665 params.pSalt = salt ? (CK_BYTE_PTR)salt->data : NULL;
666 params.ulSaltLen = salt ? salt->len : 0;
667 params.pInfo = labeledIkm->data;
668 params.ulInfoLen = labeledIkm->len;
669
670 slot = PK11_GetBestSlot(CKM_EC_KEY_PAIR_GEN, NULL);
671 CHECK_FAIL(!slot);
672
673 importedIkm = PK11_ImportDataKey(slot, CKM_HKDF_DATA, PK11_OriginUnwrap,
674 CKA_DERIVE, labeledIkm, NULL);
675 CHECK_FAIL(!importedIkm);
676 prk = PK11_Derive(importedIkm, CKM_HKDF_DATA, ¶msItem,
677 CKM_HKDF_DERIVE, CKA_DERIVE, 0);
678 CHECK_FAIL(!prk);
679 rv = PK11_ExtractKeyValue(prk);
680 CHECK_RV(rv);
681 borrowed = PK11_GetKeyData(prk);
682 CHECK_FAIL(!borrowed);
683 outDerived = SECITEM_DupItem(borrowed);
684 CHECK_FAIL(!outDerived);
685
686 *out = outDerived;
687
688 CLEANUP:
689 PK11_FreeSymKey(importedIkm);
690 PK11_FreeSymKey(prk);
691 SECITEM_FreeItem(labeledIkm, PR_TRUE);
692 if (slot) {
693 PK11_FreeSlot(slot);
694 }
695 return rv;
696 }
697
698 static SECStatus
pk11_hpke_LabeledExtract(const HpkeContext * cx,PK11SymKey * salt,const SECItem * suiteId,const char * label,CK_MECHANISM_TYPE hashMech,unsigned int labelLen,PK11SymKey * ikm,PK11SymKey ** out)699 pk11_hpke_LabeledExtract(const HpkeContext *cx, PK11SymKey *salt,
700 const SECItem *suiteId, const char *label, CK_MECHANISM_TYPE hashMech,
701 unsigned int labelLen, PK11SymKey *ikm, PK11SymKey **out)
702 {
703 SECStatus rv = SECSuccess;
704 SECItem *innerLabel = NULL;
705 PK11SymKey *labeledIkm = NULL;
706 PK11SymKey *prk = NULL;
707 CK_HKDF_PARAMS params = { 0 };
708 CK_KEY_DERIVATION_STRING_DATA labelData;
709 SECItem labelDataItem = { siBuffer, NULL, 0 };
710 SECItem paramsItem = { siBuffer, (unsigned char *)¶ms,
711 sizeof(params) };
712 PORT_Assert(cx && ikm && label && labelLen && out && suiteId);
713
714 innerLabel = pk11_hpke_MakeExtractLabel(V1_LABEL, strlen(V1_LABEL), label, labelLen, suiteId, NULL);
715 CHECK_FAIL(!innerLabel);
716 labelData.pData = innerLabel->data;
717 labelData.ulLen = innerLabel->len;
718 labelDataItem.data = (PRUint8 *)&labelData;
719 labelDataItem.len = sizeof(labelData);
720 labeledIkm = PK11_Derive(ikm, CKM_CONCATENATE_DATA_AND_BASE,
721 &labelDataItem, CKM_GENERIC_SECRET_KEY_GEN, CKA_DERIVE, 0);
722 CHECK_FAIL(!labeledIkm);
723
724 params.bExtract = CK_TRUE;
725 params.bExpand = CK_FALSE;
726 params.prfHashMechanism = hashMech;
727 params.ulSaltType = salt ? CKF_HKDF_SALT_KEY : CKF_HKDF_SALT_NULL;
728 params.hSaltKey = salt ? PK11_GetSymKeyHandle(salt) : CK_INVALID_HANDLE;
729
730 prk = PK11_Derive(labeledIkm, CKM_HKDF_DERIVE, ¶msItem,
731 CKM_HKDF_DERIVE, CKA_DERIVE, 0);
732 CHECK_FAIL(!prk);
733 *out = prk;
734
735 CLEANUP:
736 PK11_FreeSymKey(labeledIkm);
737 SECITEM_ZfreeItem(innerLabel, PR_TRUE);
738 return rv;
739 }
740
741 static SECStatus
pk11_hpke_LabeledExpand(const HpkeContext * cx,PK11SymKey * prk,const SECItem * suiteId,const char * label,unsigned int labelLen,const SECItem * info,unsigned int L,CK_MECHANISM_TYPE hashMech,PK11SymKey ** outKey,SECItem ** outItem)742 pk11_hpke_LabeledExpand(const HpkeContext *cx, PK11SymKey *prk, const SECItem *suiteId,
743 const char *label, unsigned int labelLen, const SECItem *info,
744 unsigned int L, CK_MECHANISM_TYPE hashMech, PK11SymKey **outKey,
745 SECItem **outItem)
746 {
747 SECStatus rv = SECSuccess;
748 CK_MECHANISM_TYPE keyMech;
749 CK_MECHANISM_TYPE deriveMech;
750 CK_HKDF_PARAMS params = { 0 };
751 PK11SymKey *derivedKey = NULL;
752 SECItem *labeledInfoItem = NULL;
753 SECItem paramsItem = { siBuffer, (unsigned char *)¶ms,
754 sizeof(params) };
755 SECItem *derivedKeyData;
756 PRUint8 encodedL[2];
757 PRUint8 *walker = encodedL;
758 size_t len;
759 PORT_Assert(cx && prk && label && (!!outKey != !!outItem));
760
761 walker = encodeNumber(L, walker, 2);
762 len = info ? info->len : 0;
763 len += sizeof(encodedL) + strlen(V1_LABEL) + suiteId->len + labelLen;
764 labeledInfoItem = SECITEM_AllocItem(NULL, NULL, len);
765 CHECK_FAIL(!labeledInfoItem);
766
767 walker = labeledInfoItem->data;
768 PORT_Memcpy(walker, encodedL, sizeof(encodedL));
769 walker += sizeof(encodedL);
770 PORT_Memcpy(walker, V1_LABEL, strlen(V1_LABEL));
771 walker += strlen(V1_LABEL);
772 PORT_Memcpy(walker, suiteId->data, suiteId->len);
773 walker += suiteId->len;
774 PORT_Memcpy(walker, label, labelLen);
775 walker += labelLen;
776 if (info) {
777 PORT_Memcpy(walker, info->data, info->len);
778 }
779
780 params.bExtract = CK_FALSE;
781 params.bExpand = CK_TRUE;
782 params.prfHashMechanism = hashMech;
783 params.ulSaltType = CKF_HKDF_SALT_NULL;
784 params.pInfo = labeledInfoItem->data;
785 params.ulInfoLen = labeledInfoItem->len;
786 deriveMech = outItem ? CKM_HKDF_DATA : CKM_HKDF_DERIVE;
787 /* If we're expanding to the encryption key use the appropriate mechanism. */
788 keyMech = (label && !strcmp(KEY_LABEL, label)) ? cx->aeadParams->mech : CKM_HKDF_DERIVE;
789
790 derivedKey = PK11_Derive(prk, deriveMech, ¶msItem, keyMech, CKA_DERIVE, L);
791 CHECK_FAIL(!derivedKey);
792
793 if (outItem) {
794 /* Don't allow export of real keys. */
795 CHECK_FAIL_ERR(deriveMech != CKM_HKDF_DATA, SEC_ERROR_LIBRARY_FAILURE);
796 rv = PK11_ExtractKeyValue(derivedKey);
797 CHECK_RV(rv);
798 derivedKeyData = PK11_GetKeyData(derivedKey);
799 CHECK_FAIL_ERR((!derivedKeyData), SEC_ERROR_NO_KEY);
800 *outItem = SECITEM_DupItem(derivedKeyData);
801 CHECK_FAIL(!*outItem);
802 PK11_FreeSymKey(derivedKey);
803 } else {
804 *outKey = derivedKey;
805 }
806
807 CLEANUP:
808 if (rv != SECSuccess) {
809 PK11_FreeSymKey(derivedKey);
810 }
811 SECITEM_ZfreeItem(labeledInfoItem, PR_TRUE);
812 return rv;
813 }
814
815 static SECStatus
pk11_hpke_ExtractAndExpand(const HpkeContext * cx,PK11SymKey * ikm,const SECItem * kemContext,PK11SymKey ** out)816 pk11_hpke_ExtractAndExpand(const HpkeContext *cx, PK11SymKey *ikm,
817 const SECItem *kemContext, PK11SymKey **out)
818 {
819 SECStatus rv;
820 PK11SymKey *eaePrk = NULL;
821 PK11SymKey *sharedSecret = NULL;
822 PRUint8 suiteIdBuf[5];
823 PRUint8 *walker;
824 PORT_Memcpy(suiteIdBuf, KEM_LABEL, strlen(KEM_LABEL));
825 SECItem suiteIdItem = { siBuffer, suiteIdBuf, sizeof(suiteIdBuf) };
826 PORT_Assert(cx && ikm && kemContext && out);
827
828 walker = &suiteIdBuf[3];
829 walker = encodeNumber(cx->kemParams->id, walker, 2);
830
831 rv = pk11_hpke_LabeledExtract(cx, NULL, &suiteIdItem, EAE_PRK_LABEL,
832 cx->kemParams->hashMech, strlen(EAE_PRK_LABEL),
833 ikm, &eaePrk);
834 CHECK_RV(rv);
835
836 rv = pk11_hpke_LabeledExpand(cx, eaePrk, &suiteIdItem, SH_SEC_LABEL, strlen(SH_SEC_LABEL),
837 kemContext, cx->kemParams->Nsecret, cx->kemParams->hashMech,
838 &sharedSecret, NULL);
839 CHECK_RV(rv);
840 *out = sharedSecret;
841
842 CLEANUP:
843 if (rv != SECSuccess) {
844 PK11_FreeSymKey(sharedSecret);
845 }
846 PK11_FreeSymKey(eaePrk);
847 return rv;
848 }
849
850 static SECStatus
pk11_hpke_Encap(HpkeContext * cx,const SECKEYPublicKey * pkE,SECKEYPrivateKey * skE,SECKEYPublicKey * pkR)851 pk11_hpke_Encap(HpkeContext *cx, const SECKEYPublicKey *pkE, SECKEYPrivateKey *skE,
852 SECKEYPublicKey *pkR)
853 {
854 SECStatus rv;
855 PK11SymKey *dh = NULL;
856 SECItem *kemContext = NULL;
857 SECItem *encPkR = NULL;
858 unsigned int tmpLen;
859
860 PORT_Assert(cx && skE && pkE && pkR);
861
862 rv = pk11_hpke_CheckKeys(cx, pkE, skE);
863 CHECK_RV(rv);
864 rv = pk11_hpke_CheckKeys(cx, pkR, NULL);
865 CHECK_RV(rv);
866
867 dh = PK11_PubDeriveWithKDF(skE, pkR, PR_FALSE, NULL, NULL, CKM_ECDH1_DERIVE,
868 CKM_SHA512_HMAC /* unused */, CKA_DERIVE, 0,
869 CKD_NULL, NULL, NULL);
870 CHECK_FAIL(!dh);
871
872 /* Encapsulate our sender public key. Many use cases
873 * (including ECH) require that the application fetch
874 * this value, so do it once and store into the cx. */
875 rv = PK11_HPKE_Serialize(pkE, NULL, &tmpLen, 0);
876 CHECK_RV(rv);
877 cx->encapPubKey = SECITEM_AllocItem(NULL, NULL, tmpLen);
878 CHECK_FAIL(!cx->encapPubKey);
879 rv = PK11_HPKE_Serialize(pkE, cx->encapPubKey->data,
880 &cx->encapPubKey->len, cx->encapPubKey->len);
881 CHECK_RV(rv);
882
883 rv = PK11_HPKE_Serialize(pkR, NULL, &tmpLen, 0);
884 CHECK_RV(rv);
885
886 kemContext = SECITEM_AllocItem(NULL, NULL, cx->encapPubKey->len + tmpLen);
887 CHECK_FAIL(!kemContext);
888
889 PORT_Memcpy(kemContext->data, cx->encapPubKey->data, cx->encapPubKey->len);
890 rv = PK11_HPKE_Serialize(pkR, &kemContext->data[cx->encapPubKey->len], &tmpLen, tmpLen);
891 CHECK_RV(rv);
892
893 rv = pk11_hpke_ExtractAndExpand(cx, dh, kemContext, &cx->sharedSecret);
894 CHECK_RV(rv);
895
896 CLEANUP:
897 if (rv != SECSuccess) {
898 PK11_FreeSymKey(cx->sharedSecret);
899 cx->sharedSecret = NULL;
900 }
901 SECITEM_FreeItem(encPkR, PR_TRUE);
902 SECITEM_FreeItem(kemContext, PR_TRUE);
903 PK11_FreeSymKey(dh);
904 return rv;
905 }
906
907 SECStatus
PK11_HPKE_ExportSecret(const HpkeContext * cx,const SECItem * info,unsigned int L,PK11SymKey ** out)908 PK11_HPKE_ExportSecret(const HpkeContext *cx, const SECItem *info, unsigned int L,
909 PK11SymKey **out)
910 {
911 SECStatus rv;
912 PK11SymKey *exported;
913 PRUint8 suiteIdBuf[10];
914 PRUint8 *walker;
915 PORT_Memcpy(suiteIdBuf, HPKE_LABEL, strlen(HPKE_LABEL));
916 SECItem suiteIdItem = { siBuffer, suiteIdBuf, sizeof(suiteIdBuf) };
917
918 /* Arbitrary info length limit well under the specified max. */
919 if (!cx || !info || (!info->data && info->len) || info->len > 0xFFFF ||
920 !L || (L > 255 * cx->kdfParams->Nh)) {
921 PORT_SetError(SEC_ERROR_INVALID_ARGS);
922 return SECFailure;
923 }
924
925 walker = &suiteIdBuf[4];
926 walker = encodeNumber(cx->kemParams->id, walker, 2);
927 walker = encodeNumber(cx->kdfParams->id, walker, 2);
928 walker = encodeNumber(cx->aeadParams->id, walker, 2);
929
930 rv = pk11_hpke_LabeledExpand(cx, cx->exporterSecret, &suiteIdItem, SEC_LABEL,
931 strlen(SEC_LABEL), info, L, cx->kdfParams->mech,
932 &exported, NULL);
933 CHECK_RV(rv);
934 *out = exported;
935
936 CLEANUP:
937 return rv;
938 }
939
940 static SECStatus
pk11_hpke_Decap(HpkeContext * cx,const SECKEYPublicKey * pkR,SECKEYPrivateKey * skR,const SECItem * encS)941 pk11_hpke_Decap(HpkeContext *cx, const SECKEYPublicKey *pkR, SECKEYPrivateKey *skR,
942 const SECItem *encS)
943 {
944 SECStatus rv;
945 PK11SymKey *dh = NULL;
946 SECItem *encR = NULL;
947 SECItem *kemContext = NULL;
948 SECKEYPublicKey *pkS = NULL;
949 unsigned int tmpLen;
950
951 if (!cx || !skR || !pkR || !encS || !encS->data || !encS->len) {
952 PORT_SetError(SEC_ERROR_INVALID_ARGS);
953 return SECFailure;
954 }
955
956 rv = PK11_HPKE_Deserialize(cx, encS->data, encS->len, &pkS);
957 CHECK_RV(rv);
958
959 rv = pk11_hpke_CheckKeys(cx, pkR, skR);
960 CHECK_RV(rv);
961 rv = pk11_hpke_CheckKeys(cx, pkS, NULL);
962 CHECK_RV(rv);
963
964 dh = PK11_PubDeriveWithKDF(skR, pkS, PR_FALSE, NULL, NULL, CKM_ECDH1_DERIVE,
965 CKM_SHA512_HMAC /* unused */, CKA_DERIVE, 0,
966 CKD_NULL, NULL, NULL);
967 CHECK_FAIL(!dh);
968
969 /* kem_context = concat(enc, pkRm) */
970 rv = PK11_HPKE_Serialize(pkR, NULL, &tmpLen, 0);
971 CHECK_RV(rv);
972
973 kemContext = SECITEM_AllocItem(NULL, NULL, encS->len + tmpLen);
974 CHECK_FAIL(!kemContext);
975
976 PORT_Memcpy(kemContext->data, encS->data, encS->len);
977 rv = PK11_HPKE_Serialize(pkR, &kemContext->data[encS->len], &tmpLen,
978 kemContext->len - encS->len);
979 CHECK_RV(rv);
980 rv = pk11_hpke_ExtractAndExpand(cx, dh, kemContext, &cx->sharedSecret);
981 CHECK_RV(rv);
982
983 /* Store the sender serialized public key, which
984 * may be required by application use cases. */
985 cx->encapPubKey = SECITEM_DupItem(encS);
986 CHECK_FAIL(!cx->encapPubKey);
987
988 CLEANUP:
989 if (rv != SECSuccess) {
990 PK11_FreeSymKey(cx->sharedSecret);
991 cx->sharedSecret = NULL;
992 }
993 PK11_FreeSymKey(dh);
994 SECKEY_DestroyPublicKey(pkS);
995 SECITEM_FreeItem(encR, PR_TRUE);
996 SECITEM_ZfreeItem(kemContext, PR_TRUE);
997 return rv;
998 }
999
1000 const SECItem *
PK11_HPKE_GetEncapPubKey(const HpkeContext * cx)1001 PK11_HPKE_GetEncapPubKey(const HpkeContext *cx)
1002 {
1003 if (!cx) {
1004 return NULL;
1005 }
1006 return cx->encapPubKey;
1007 }
1008
1009 static SECStatus
pk11_hpke_KeySchedule(HpkeContext * cx,const SECItem * info)1010 pk11_hpke_KeySchedule(HpkeContext *cx, const SECItem *info)
1011 {
1012 SECStatus rv;
1013 SECItem contextItem = { siBuffer, NULL, 0 };
1014 unsigned int len;
1015 unsigned int off;
1016 PK11SymKey *secret = NULL;
1017 SECItem *pskIdHash = NULL;
1018 SECItem *infoHash = NULL;
1019 PRUint8 suiteIdBuf[10];
1020 PRUint8 *walker;
1021 PORT_Memcpy(suiteIdBuf, HPKE_LABEL, strlen(HPKE_LABEL));
1022 SECItem suiteIdItem = { siBuffer, suiteIdBuf, sizeof(suiteIdBuf) };
1023 PORT_Assert(cx && info && cx->psk && cx->pskId);
1024
1025 walker = &suiteIdBuf[4];
1026 walker = encodeNumber(cx->kemParams->id, walker, 2);
1027 walker = encodeNumber(cx->kdfParams->id, walker, 2);
1028 walker = encodeNumber(cx->aeadParams->id, walker, 2);
1029
1030 rv = pk11_hpke_LabeledExtractData(cx, NULL, &suiteIdItem, PSK_ID_LABEL,
1031 strlen(PSK_ID_LABEL), cx->pskId, &pskIdHash);
1032 CHECK_RV(rv);
1033 rv = pk11_hpke_LabeledExtractData(cx, NULL, &suiteIdItem, INFO_LABEL,
1034 strlen(INFO_LABEL), info, &infoHash);
1035 CHECK_RV(rv);
1036
1037 // Make the context string
1038 len = sizeof(cx->mode) + pskIdHash->len + infoHash->len;
1039 CHECK_FAIL(!SECITEM_AllocItem(NULL, &contextItem, len));
1040 off = 0;
1041 PORT_Memcpy(&contextItem.data[off], &cx->mode, sizeof(cx->mode));
1042 off += sizeof(cx->mode);
1043 PORT_Memcpy(&contextItem.data[off], pskIdHash->data, pskIdHash->len);
1044 off += pskIdHash->len;
1045 PORT_Memcpy(&contextItem.data[off], infoHash->data, infoHash->len);
1046 off += infoHash->len;
1047
1048 // Compute the keys
1049 rv = pk11_hpke_LabeledExtract(cx, cx->sharedSecret, &suiteIdItem, SECRET_LABEL,
1050 cx->kdfParams->mech, strlen(SECRET_LABEL),
1051 cx->psk, &secret);
1052 CHECK_RV(rv);
1053 rv = pk11_hpke_LabeledExpand(cx, secret, &suiteIdItem, KEY_LABEL, strlen(KEY_LABEL),
1054 &contextItem, cx->aeadParams->Nk, cx->kdfParams->mech,
1055 &cx->key, NULL);
1056 CHECK_RV(rv);
1057 rv = pk11_hpke_LabeledExpand(cx, secret, &suiteIdItem, NONCE_LABEL, strlen(NONCE_LABEL),
1058 &contextItem, cx->aeadParams->Nn, cx->kdfParams->mech,
1059 NULL, &cx->baseNonce);
1060 CHECK_RV(rv);
1061 rv = pk11_hpke_LabeledExpand(cx, secret, &suiteIdItem, EXP_LABEL, strlen(EXP_LABEL),
1062 &contextItem, cx->kdfParams->Nh, cx->kdfParams->mech,
1063 &cx->exporterSecret, NULL);
1064 CHECK_RV(rv);
1065
1066 CLEANUP:
1067 /* If !SECSuccess, callers will tear down the context. */
1068 PK11_FreeSymKey(secret);
1069 SECITEM_FreeItem(&contextItem, PR_FALSE);
1070 SECITEM_FreeItem(infoHash, PR_TRUE);
1071 SECITEM_FreeItem(pskIdHash, PR_TRUE);
1072 return rv;
1073 }
1074
1075 SECStatus
PK11_HPKE_SetupR(HpkeContext * cx,const SECKEYPublicKey * pkR,SECKEYPrivateKey * skR,const SECItem * enc,const SECItem * info)1076 PK11_HPKE_SetupR(HpkeContext *cx, const SECKEYPublicKey *pkR, SECKEYPrivateKey *skR,
1077 const SECItem *enc, const SECItem *info)
1078 {
1079 SECStatus rv;
1080 SECItem empty = { siBuffer, NULL, 0 };
1081
1082 CHECK_FAIL_ERR((!cx || !skR || !info || !enc || !enc->data || !enc->len),
1083 SEC_ERROR_INVALID_ARGS);
1084 /* Already setup */
1085 CHECK_FAIL_ERR((cx->aeadContext), SEC_ERROR_INVALID_STATE);
1086
1087 rv = pk11_hpke_Decap(cx, pkR, skR, enc);
1088 CHECK_RV(rv);
1089 rv = pk11_hpke_KeySchedule(cx, info);
1090 CHECK_RV(rv);
1091
1092 /* Store the key context for subsequent calls to Open().
1093 * PK11_CreateContextBySymKey refs the key internally. */
1094 PORT_Assert(cx->key);
1095 cx->aeadContext = PK11_CreateContextBySymKey(cx->aeadParams->mech,
1096 CKA_NSS_MESSAGE | CKA_DECRYPT,
1097 cx->key, &empty);
1098 CHECK_FAIL_ERR((!cx->aeadContext), SEC_ERROR_LIBRARY_FAILURE);
1099
1100 CLEANUP:
1101 if (rv != SECSuccess) {
1102 /* Clear everything past NewContext. */
1103 PK11_HPKE_DestroyContext(cx, PR_FALSE);
1104 }
1105 return rv;
1106 }
1107
1108 SECStatus
PK11_HPKE_SetupS(HpkeContext * cx,const SECKEYPublicKey * pkE,SECKEYPrivateKey * skE,SECKEYPublicKey * pkR,const SECItem * info)1109 PK11_HPKE_SetupS(HpkeContext *cx, const SECKEYPublicKey *pkE, SECKEYPrivateKey *skE,
1110 SECKEYPublicKey *pkR, const SECItem *info)
1111 {
1112 SECStatus rv;
1113 SECItem empty = { siBuffer, NULL, 0 };
1114 SECKEYPublicKey *tmpPkE = NULL;
1115 SECKEYPrivateKey *tmpSkE = NULL;
1116 CHECK_FAIL_ERR((!cx || !pkR || !info || (!!skE != !!pkE)), SEC_ERROR_INVALID_ARGS);
1117 /* Already setup */
1118 CHECK_FAIL_ERR((cx->aeadContext), SEC_ERROR_INVALID_STATE);
1119
1120 /* If NULL was passed for the local keypair, generate one. */
1121 if (skE == NULL) {
1122 rv = pk11_hpke_GenerateKeyPair(cx, &tmpPkE, &tmpSkE);
1123 if (rv != SECSuccess) {
1124 /* Code set */
1125 return SECFailure;
1126 }
1127 rv = pk11_hpke_Encap(cx, tmpPkE, tmpSkE, pkR);
1128 } else {
1129 rv = pk11_hpke_Encap(cx, pkE, skE, pkR);
1130 }
1131 CHECK_RV(rv);
1132
1133 SECItem defaultInfo = { siBuffer, NULL, 0 };
1134 if (!info || !info->data) {
1135 info = &defaultInfo;
1136 }
1137 rv = pk11_hpke_KeySchedule(cx, info);
1138 CHECK_RV(rv);
1139
1140 PORT_Assert(cx->key);
1141 cx->aeadContext = PK11_CreateContextBySymKey(cx->aeadParams->mech,
1142 CKA_NSS_MESSAGE | CKA_ENCRYPT,
1143 cx->key, &empty);
1144 CHECK_FAIL_ERR((!cx->aeadContext), SEC_ERROR_LIBRARY_FAILURE);
1145
1146 CLEANUP:
1147 if (rv != SECSuccess) {
1148 /* Clear everything past NewContext. */
1149 PK11_HPKE_DestroyContext(cx, PR_FALSE);
1150 }
1151 SECKEY_DestroyPrivateKey(tmpSkE);
1152 SECKEY_DestroyPublicKey(tmpPkE);
1153 return rv;
1154 }
1155
1156 SECStatus
PK11_HPKE_Seal(HpkeContext * cx,const SECItem * aad,const SECItem * pt,SECItem ** out)1157 PK11_HPKE_Seal(HpkeContext *cx, const SECItem *aad, const SECItem *pt,
1158 SECItem **out)
1159 {
1160 SECStatus rv;
1161 PRUint8 ivOut[12] = { 0 };
1162 SECItem *ct = NULL;
1163 size_t maxOut;
1164 unsigned char tagBuf[HASH_LENGTH_MAX];
1165 size_t tagLen;
1166 unsigned int fixedBits;
1167
1168 /* aad may be NULL, PT may be zero-length but not NULL. */
1169 if (!cx || !cx->aeadContext ||
1170 (aad && aad->len && !aad->data) ||
1171 !pt || (pt->len && !pt->data) ||
1172 !out) {
1173 PORT_SetError(SEC_ERROR_INVALID_ARGS);
1174 return SECFailure;
1175 }
1176
1177 PORT_Assert(cx->baseNonce->len == sizeof(ivOut));
1178 PORT_Memcpy(ivOut, cx->baseNonce->data, cx->baseNonce->len);
1179
1180 tagLen = cx->aeadParams->tagLen;
1181 maxOut = pt->len + tagLen;
1182 fixedBits = (cx->baseNonce->len - 8) * 8;
1183 ct = SECITEM_AllocItem(NULL, NULL, maxOut);
1184 CHECK_FAIL(!ct);
1185
1186 rv = PK11_AEADOp(cx->aeadContext,
1187 CKG_GENERATE_COUNTER_XOR, fixedBits,
1188 ivOut, sizeof(ivOut),
1189 aad ? aad->data : NULL,
1190 aad ? aad->len : 0,
1191 ct->data, (int *)&ct->len, maxOut,
1192 tagBuf, tagLen,
1193 pt->data, pt->len);
1194 CHECK_RV(rv);
1195 CHECK_FAIL_ERR((ct->len > maxOut - tagLen), SEC_ERROR_LIBRARY_FAILURE);
1196
1197 /* Append the tag to the ciphertext. */
1198 PORT_Memcpy(&ct->data[ct->len], tagBuf, tagLen);
1199 ct->len += tagLen;
1200 *out = ct;
1201
1202 CLEANUP:
1203 if (rv != SECSuccess) {
1204 SECITEM_ZfreeItem(ct, PR_TRUE);
1205 }
1206 return rv;
1207 }
1208
1209 /* PKCS #11 defines the IV generator function to be ignored on
1210 * decrypt (i.e. it uses the nonce input, as provided, as the IV).
1211 * The sequence number is kept independently on each endpoint and
1212 * the XORed IV is not transmitted, so we have to do our own IV
1213 * construction XOR outside of the token. */
1214 static SECStatus
pk11_hpke_makeIv(HpkeContext * cx,PRUint8 * iv,size_t ivLen)1215 pk11_hpke_makeIv(HpkeContext *cx, PRUint8 *iv, size_t ivLen)
1216 {
1217 unsigned int counterLen = sizeof(cx->sequenceNumber);
1218 PORT_Assert(cx->baseNonce->len == ivLen);
1219 PORT_Assert(counterLen == 8);
1220 if (cx->sequenceNumber == PR_UINT64(0xffffffffffffffff)) {
1221 /* Overflow */
1222 PORT_SetError(SEC_ERROR_INVALID_KEY);
1223 return SECFailure;
1224 }
1225
1226 PORT_Memcpy(iv, cx->baseNonce->data, cx->baseNonce->len);
1227 for (size_t i = 0; i < counterLen; i++) {
1228 iv[cx->baseNonce->len - 1 - i] ^=
1229 PORT_GET_BYTE_BE(cx->sequenceNumber,
1230 counterLen - 1 - i, counterLen);
1231 }
1232 return SECSuccess;
1233 }
1234
1235 SECStatus
PK11_HPKE_Open(HpkeContext * cx,const SECItem * aad,const SECItem * ct,SECItem ** out)1236 PK11_HPKE_Open(HpkeContext *cx, const SECItem *aad,
1237 const SECItem *ct, SECItem **out)
1238 {
1239 SECStatus rv;
1240 PRUint8 constructedNonce[12] = { 0 };
1241 unsigned int tagLen;
1242 SECItem *pt = NULL;
1243
1244 /* aad may be NULL, CT may be zero-length but not NULL. */
1245 if ((!cx || !cx->aeadContext || !ct || !out) ||
1246 (aad && aad->len && !aad->data) ||
1247 (!ct->data || (ct->data && !ct->len))) {
1248 PORT_SetError(SEC_ERROR_INVALID_ARGS);
1249 return SECFailure;
1250 }
1251 tagLen = cx->aeadParams->tagLen;
1252 CHECK_FAIL_ERR((ct->len < tagLen), SEC_ERROR_INVALID_ARGS);
1253
1254 pt = SECITEM_AllocItem(NULL, NULL, ct->len);
1255 CHECK_FAIL(!pt);
1256
1257 rv = pk11_hpke_makeIv(cx, constructedNonce, sizeof(constructedNonce));
1258 CHECK_RV(rv);
1259
1260 rv = PK11_AEADOp(cx->aeadContext, CKG_NO_GENERATE, 0,
1261 constructedNonce, sizeof(constructedNonce),
1262 aad ? aad->data : NULL,
1263 aad ? aad->len : 0,
1264 pt->data, (int *)&pt->len, pt->len,
1265 &ct->data[ct->len - tagLen], tagLen,
1266 ct->data, ct->len - tagLen);
1267 CHECK_RV(rv);
1268 cx->sequenceNumber++;
1269 *out = pt;
1270
1271 CLEANUP:
1272 if (rv != SECSuccess) {
1273 SECITEM_ZfreeItem(pt, PR_TRUE);
1274 }
1275 return rv;
1276 }
1277