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 *)&params,
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, &paramsItem,
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 *)&params,
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, &paramsItem,
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 *)&params,
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, &paramsItem, 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