Coverage Report

Created: 2026-01-22 06:19

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