Coverage Report

Created: 2025-09-17 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/softoken/kem.c
Line
Count
Source
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "blapi.h"
6
#include "kem.h"
7
#include "pkcs11i.h"
8
#include "pkcs11n.h"
9
#include "secerr.h"
10
#include "secitem.h"
11
#include "secport.h"
12
#include "softoken.h"
13
14
/* change to the largest KEM Secret Bytes value supported */
15
#define MAX_SHARED_SECRET_BYTES KYBER_SHARED_SECRET_BYTES
16
17
KyberParams
18
sftk_kyber_PK11ParamToInternal(CK_ML_KEM_PARAMETER_SET_TYPE pk11ParamSet)
19
350
{
20
350
    switch (pk11ParamSet) {
21
0
#ifndef NSS_DISABLE_KYBER
22
0
        case CKP_NSS_KYBER_768_ROUND3:
23
0
            return params_kyber768_round3;
24
0
#endif
25
202
        case CKP_NSS_ML_KEM_768:
26
350
        case CKP_ML_KEM_768:
27
350
            return params_ml_kem768;
28
0
        default:
29
0
            return params_kyber_invalid;
30
350
    }
31
350
}
32
33
SECItem *
34
sftk_kyber_AllocPubKeyItem(KyberParams params, SECItem *pubkey)
35
140
{
36
140
    switch (params) {
37
0
#ifndef NSS_DISABLE_KYBER
38
0
        case params_kyber768_round3:
39
0
        case params_kyber768_round3_test_mode:
40
0
#endif
41
140
        case params_ml_kem768:
42
140
        case params_ml_kem768_test_mode:
43
140
            return SECITEM_AllocItem(NULL, pubkey, KYBER768_PUBLIC_KEY_BYTES);
44
0
        default:
45
0
            return NULL;
46
140
    }
47
140
}
48
49
SECItem *
50
sftk_kyber_AllocPrivKeyItem(KyberParams params, SECItem *privkey)
51
140
{
52
140
    switch (params) {
53
0
#ifndef NSS_DISABLE_KYBER
54
0
        case params_kyber768_round3:
55
0
        case params_kyber768_round3_test_mode:
56
0
#endif
57
140
        case params_ml_kem768:
58
140
        case params_ml_kem768_test_mode:
59
140
            return SECITEM_AllocItem(NULL, privkey, KYBER768_PRIVATE_KEY_BYTES);
60
0
        default:
61
0
            return NULL;
62
140
    }
63
140
}
64
65
SECItem *
66
sftk_kyber_AllocCiphertextItem(KyberParams params, SECItem *ciphertext)
67
0
{
68
0
    switch (params) {
69
0
#ifndef NSS_DISABLE_KYBER
70
0
        case params_kyber768_round3:
71
0
        case params_kyber768_round3_test_mode:
72
0
#endif
73
0
        case params_ml_kem768:
74
0
        case params_ml_kem768_test_mode:
75
0
            return SECITEM_AllocItem(NULL, ciphertext, KYBER768_CIPHERTEXT_BYTES);
76
0
        default:
77
0
            return NULL;
78
0
    }
79
0
}
80
81
static PRBool
82
sftk_kem_ValidateMechanism(CK_MECHANISM_PTR pMechanism)
83
412
{
84
412
    if (!pMechanism) {
85
0
        return PR_FALSE;
86
0
    }
87
412
    switch (pMechanism->mechanism) {
88
0
#ifndef NSS_DISABLE_KYBER
89
0
        case CKM_NSS_KYBER:
90
0
#endif
91
0
        case CKM_NSS_ML_KEM:
92
412
        case CKM_ML_KEM:
93
412
            return PR_TRUE;
94
0
        default:
95
0
            return PR_FALSE;
96
412
    }
97
412
}
98
99
/* this is a generic call the returns the paramSet as a CKU_LONG. if the
100
 * param set is in a different attribute or mechanism structure, that would
101
 * be based on the mechanism. The meaning of the paramter set is alway
102
 * mechanism specific */
103
static CK_RV
104
sftk_kem_getParamSet(CK_MECHANISM_PTR pMechanism, SFTKObject *key,
105
                     CK_ULONG *paramSet)
106
412
{
107
412
    CK_RV crv = CKR_MECHANISM_INVALID;
108
109
412
    switch (pMechanism->mechanism) {
110
0
#ifndef NSS_DISABLE_KYBER
111
0
        case CKM_NSS_KYBER:
112
0
#endif
113
0
        case CKM_NSS_ML_KEM:
114
            /* CKM_NSS_KYBER and CKM_NSS_ML_KEM has cases were we were
115
             * given parameters form the application. Retain that symantic for
116
             * compatibility */
117
0
            if ((pMechanism->pParameter) &&
118
0
                pMechanism->ulParameterLen == sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)) {
119
0
                PR_STATIC_ASSERT(sizeof(CK_ML_KEM_PARAMETER_SET_TYPE) == sizeof(CK_LONG));
120
0
                *paramSet = *(CK_ULONG *)pMechanism->pParameter;
121
0
                crv = CKR_OK;
122
0
                break;
123
0
            }
124
            /* fall through to the official PKCS #11 way of getting the
125
             * parameter set */
126
412
        case CKM_ML_KEM:
127
412
            crv = sftk_GetULongAttribute(key, CKA_PARAMETER_SET, paramSet);
128
412
            if (crv == CKR_OK) {
129
8
                break;
130
8
            }
131
            /* we accept all key types a equivalent here, so the parameter
132
             * set could be attached to the old vendor specific attribute.
133
             * We might use the vendor specific key when bypassing pairwise
134
             * consistency checks */
135
404
            crv = sftk_GetULongAttribute(key, CKA_NSS_PARAMETER_SET, paramSet);
136
404
            break;
137
0
        default:
138
0
            break;
139
412
    }
140
412
    return crv;
141
412
}
142
143
static CK_ULONG
144
sftk_kem_CiphertextLen(CK_MECHANISM_PTR pMechanism, CK_ULONG paramSet)
145
412
{
146
    /* the switch here is redundant now, but we will eventually have
147
     * other unrelated KEM operations and the meaning of paramSet
148
     * is dependent of the mechanism type (in general). Since there
149
     * is not overlap between the Vendor specific mechanisms here
150
     * and the stand ones, we'll just accept them all here. */
151
412
    switch (pMechanism->mechanism) {
152
0
#ifndef NSS_DISABLE_KYBER
153
0
        case CKM_NSS_KYBER:
154
0
#endif
155
0
        case CKM_NSS_ML_KEM:
156
412
        case CKM_ML_KEM:
157
412
            switch (paramSet) {
158
0
#ifndef NSS_DISABLE_KYBER
159
0
                case CKP_NSS_KYBER_768_ROUND3:
160
0
#endif
161
404
                case CKP_NSS_ML_KEM_768:
162
412
                case CKP_ML_KEM_768:
163
412
                    return KYBER768_CIPHERTEXT_BYTES;
164
0
                default:
165
0
                    break;
166
412
            }
167
0
        default:
168
0
            break;
169
412
    }
170
0
    return 0;
171
412
}
172
173
/* C_Encapsulate takes a public encapsulation key hPublicKey, a secret
174
 * phKey, and outputs a ciphertext (i.e. encapsulaton) of this secret in
175
 * pCiphertext. */
176
CK_RV
177
NSC_EncapsulateKey(CK_SESSION_HANDLE hSession,
178
                   CK_MECHANISM_PTR pMechanism,
179
                   CK_OBJECT_HANDLE hPublicKey,
180
                   CK_ATTRIBUTE_PTR pTemplate,
181
                   CK_ULONG ulAttributeCount,
182
                   /* out */ CK_BYTE_PTR pCiphertext,
183
                   /* out */ CK_ULONG_PTR pulCiphertextLen,
184
                   /* out */ CK_OBJECT_HANDLE_PTR phKey)
185
404
{
186
404
    SFTKSession *session = NULL;
187
404
    SFTKSlot *slot = NULL;
188
189
404
    SFTKObject *key = NULL;
190
191
404
    SFTKObject *encapsulationKeyObject = NULL;
192
404
    SFTKAttribute *encapsulationKey = NULL;
193
194
404
    CK_RV crv;
195
404
    SFTKFreeStatus status;
196
404
    CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle
197
                            * different param set types based on the
198
                            * Mechanism value */
199
404
    KyberParams kyberParams;
200
201
404
    CHECK_FORK();
202
203
404
    if (!pMechanism || !phKey || !pulCiphertextLen) {
204
0
        return CKR_ARGUMENTS_BAD;
205
0
    }
206
207
404
    if (!sftk_kem_ValidateMechanism(pMechanism)) {
208
0
        return CKR_MECHANISM_INVALID;
209
0
    }
210
404
    *phKey = CK_INVALID_HANDLE;
211
212
404
    session = sftk_SessionFromHandle(hSession);
213
404
    if (session == NULL) {
214
0
        return CKR_SESSION_HANDLE_INVALID;
215
0
    }
216
404
    slot = sftk_SlotFromSessionHandle(hSession);
217
404
    if (slot == NULL) {
218
0
        crv = CKR_SESSION_HANDLE_INVALID;
219
0
        goto cleanup;
220
0
    }
221
222
404
    key = sftk_NewObject(slot);
223
404
    if (key == NULL) {
224
0
        crv = CKR_HOST_MEMORY;
225
0
        goto cleanup;
226
0
    }
227
2.42k
    for (unsigned long int i = 0; i < ulAttributeCount; i++) {
228
2.02k
        crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
229
2.02k
        if (crv != CKR_OK) {
230
0
            goto cleanup;
231
0
        }
232
2.02k
    }
233
234
404
    encapsulationKeyObject = sftk_ObjectFromHandle(hPublicKey, session);
235
404
    if (encapsulationKeyObject == NULL) {
236
0
        crv = CKR_KEY_HANDLE_INVALID;
237
0
        goto cleanup;
238
0
    }
239
404
    encapsulationKey = sftk_FindAttribute(encapsulationKeyObject, CKA_VALUE);
240
404
    if (encapsulationKey == NULL) {
241
0
        crv = CKR_KEY_HANDLE_INVALID;
242
0
        goto cleanup;
243
0
    }
244
245
404
    crv = sftk_kem_getParamSet(pMechanism, encapsulationKeyObject, &paramSet);
246
404
    if (crv != CKR_OK) {
247
0
        goto cleanup;
248
0
    }
249
250
404
    CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism, paramSet);
251
404
    if (!pCiphertext || *pulCiphertextLen < ciphertextLen || ciphertextLen == 0) {
252
202
        *pulCiphertextLen = ciphertextLen;
253
202
        crv = CKR_BUFFER_TOO_SMALL;
254
202
        goto cleanup;
255
202
    }
256
257
202
    SECItem ciphertext = { siBuffer, pCiphertext, ciphertextLen };
258
202
    SECItem pubKey = { siBuffer, encapsulationKey->attrib.pValue, encapsulationKey->attrib.ulValueLen };
259
260
    /* The length of secretBuf can be increased if we ever support other KEMs
261
     * by changing the define at the top of this file */
262
202
    uint8_t secretBuf[MAX_SHARED_SECRET_BYTES] = { 0 };
263
202
    SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
264
265
202
    switch (pMechanism->mechanism) {
266
0
#ifndef NSS_DISABLE_KYBER
267
0
        case CKM_NSS_KYBER:
268
0
#endif
269
0
        case CKM_NSS_ML_KEM:
270
202
        case CKM_ML_KEM:
271
202
            PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES);
272
202
            kyberParams = sftk_kyber_PK11ParamToInternal(paramSet);
273
202
            SECStatus rv = Kyber_Encapsulate(kyberParams, /* seed */ NULL,
274
202
                                             &pubKey, &ciphertext, &secret);
275
202
            if (rv != SECSuccess) {
276
44
                crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
277
44
                goto cleanup;
278
44
            }
279
280
158
            crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
281
158
            if (crv != CKR_OK) {
282
0
                goto cleanup;
283
0
            }
284
285
158
            crv = sftk_handleObject(key, session);
286
158
            if (crv != CKR_OK) {
287
0
                goto cleanup;
288
0
            }
289
290
            /* We wrote the ciphertext out directly in Kyber_Encapsulate */
291
158
            *phKey = key->handle;
292
158
            *pulCiphertextLen = ciphertext.len;
293
158
            break;
294
0
        default:
295
0
            crv = CKR_MECHANISM_INVALID;
296
0
            goto cleanup;
297
202
    }
298
299
404
cleanup:
300
404
    if (session) {
301
404
        sftk_FreeSession(session);
302
404
    }
303
404
    if (key) {
304
404
        status = sftk_FreeObject(key);
305
404
        if (status == SFTK_DestroyFailure) {
306
0
            return CKR_DEVICE_ERROR;
307
0
        }
308
404
    }
309
404
    if (encapsulationKeyObject) {
310
404
        status = sftk_FreeObject(encapsulationKeyObject);
311
404
        if (status == SFTK_DestroyFailure) {
312
0
            return CKR_DEVICE_ERROR;
313
0
        }
314
404
    }
315
404
    if (encapsulationKey) {
316
404
        sftk_FreeAttribute(encapsulationKey);
317
404
    }
318
404
    return crv;
319
404
}
320
321
CK_RV
322
NSC_DecapsulateKey(CK_SESSION_HANDLE hSession,
323
                   CK_MECHANISM_PTR pMechanism,
324
                   CK_OBJECT_HANDLE hPrivateKey,
325
                   CK_ATTRIBUTE_PTR pTemplate,
326
                   CK_ULONG ulAttributeCount,
327
                   CK_BYTE_PTR pCiphertext,
328
                   CK_ULONG ulCiphertextLen,
329
                   /* out */ CK_OBJECT_HANDLE_PTR phKey)
330
8
{
331
8
    SFTKSession *session = NULL;
332
8
    SFTKSlot *slot = NULL;
333
334
8
    SFTKObject *key = NULL;
335
336
8
    SFTKObject *decapsulationKeyObject = NULL;
337
8
    SFTKAttribute *decapsulationKey = NULL;
338
8
    CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle
339
                            * different param set types based on the
340
                            * Mechanism value */
341
342
8
    CK_RV crv;
343
8
    SFTKFreeStatus status;
344
8
    KyberParams kyberParams;
345
346
8
    CHECK_FORK();
347
348
8
    if (!pMechanism || !pCiphertext || !pTemplate || !phKey) {
349
0
        return CKR_ARGUMENTS_BAD;
350
0
    }
351
352
8
    if (!sftk_kem_ValidateMechanism(pMechanism)) {
353
0
        return CKR_MECHANISM_INVALID;
354
0
    }
355
8
    *phKey = CK_INVALID_HANDLE;
356
357
8
    session = sftk_SessionFromHandle(hSession);
358
8
    if (session == NULL) {
359
0
        return CKR_SESSION_HANDLE_INVALID;
360
0
    }
361
8
    slot = sftk_SlotFromSessionHandle(hSession);
362
8
    if (slot == NULL) {
363
0
        crv = CKR_SESSION_HANDLE_INVALID;
364
0
        goto cleanup;
365
0
    }
366
367
8
    key = sftk_NewObject(slot);
368
8
    if (key == NULL) {
369
0
        crv = CKR_HOST_MEMORY;
370
0
        goto cleanup;
371
0
    }
372
48
    for (unsigned long int i = 0; i < ulAttributeCount; i++) {
373
40
        crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
374
40
        if (crv != CKR_OK) {
375
0
            goto cleanup;
376
0
        }
377
40
    }
378
379
8
    decapsulationKeyObject = sftk_ObjectFromHandle(hPrivateKey, session);
380
8
    if (decapsulationKeyObject == NULL) {
381
0
        crv = CKR_KEY_HANDLE_INVALID;
382
0
        goto cleanup;
383
0
    }
384
8
    decapsulationKey = sftk_FindAttribute(decapsulationKeyObject, CKA_VALUE);
385
8
    if (decapsulationKey == NULL) {
386
0
        crv = CKR_KEY_HANDLE_INVALID;
387
0
        goto cleanup;
388
0
    }
389
390
8
    crv = sftk_kem_getParamSet(pMechanism, decapsulationKeyObject, &paramSet);
391
8
    if (crv != CKR_OK) {
392
0
        goto cleanup;
393
0
    }
394
395
8
    CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism, paramSet);
396
8
    if (!pCiphertext || ulCiphertextLen != ciphertextLen || ciphertextLen == 0) {
397
0
        crv = CKR_ARGUMENTS_BAD;
398
0
        goto cleanup;
399
0
    }
400
401
8
    SECItem privKey = { siBuffer, decapsulationKey->attrib.pValue,
402
8
                        decapsulationKey->attrib.ulValueLen };
403
8
    SECItem ciphertext = { siBuffer, pCiphertext, ulCiphertextLen };
404
405
    /* The length of secretBuf can be increased if we ever support other KEMs
406
     * by changing the define at the top of this file */
407
8
    uint8_t secretBuf[MAX_SHARED_SECRET_BYTES] = { 0 };
408
8
    SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
409
410
8
    switch (pMechanism->mechanism) {
411
0
#ifndef NSS_DISABLE_KYBER
412
0
        case CKM_NSS_KYBER:
413
0
#endif
414
0
        case CKM_NSS_ML_KEM:
415
8
        case CKM_ML_KEM:
416
8
            kyberParams = sftk_kyber_PK11ParamToInternal(paramSet);
417
8
            PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES);
418
8
            SECStatus rv = Kyber_Decapsulate(kyberParams, &privKey,
419
8
                                             &ciphertext, &secret);
420
8
            if (rv != SECSuccess) {
421
0
                crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
422
0
                goto cleanup;
423
0
            }
424
425
8
            crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
426
8
            if (crv != CKR_OK) {
427
0
                goto cleanup;
428
0
            }
429
430
8
            crv = sftk_handleObject(key, session);
431
8
            if (crv != CKR_OK) {
432
0
                goto cleanup;
433
0
            }
434
8
            *phKey = key->handle;
435
8
            break;
436
0
        default:
437
0
            crv = CKR_MECHANISM_INVALID;
438
0
            goto cleanup;
439
8
    }
440
441
8
cleanup:
442
8
    if (session) {
443
8
        sftk_FreeSession(session);
444
8
    }
445
8
    if (key) {
446
8
        status = sftk_FreeObject(key);
447
8
        if (status == SFTK_DestroyFailure) {
448
0
            return CKR_DEVICE_ERROR;
449
0
        }
450
8
    }
451
8
    if (decapsulationKeyObject) {
452
8
        status = sftk_FreeObject(decapsulationKeyObject);
453
8
        if (status == SFTK_DestroyFailure) {
454
0
            return CKR_DEVICE_ERROR;
455
0
        }
456
8
    }
457
8
    if (decapsulationKey) {
458
8
        sftk_FreeAttribute(decapsulationKey);
459
8
    }
460
8
    return crv;
461
8
}
462
463
/* PKCS #11 final spec moved som the the arguments around (to make
464
 * NSC_EncapsulateKey and NSC_DecapsulateKey match, keep the old version for
465
 * the vendor defined for backward compatibility */
466
CK_RV
467
NSC_Encapsulate(CK_SESSION_HANDLE hSession,
468
                CK_MECHANISM_PTR pMechanism,
469
                CK_OBJECT_HANDLE hPublicKey,
470
                CK_ATTRIBUTE_PTR pTemplate,
471
                CK_ULONG ulAttributeCount,
472
                /* out */ CK_OBJECT_HANDLE_PTR phKey,
473
                /* out */ CK_BYTE_PTR pCiphertext,
474
                /* out */ CK_ULONG_PTR pulCiphertextLen)
475
0
{
476
0
    return NSC_EncapsulateKey(hSession, pMechanism, hPublicKey,
477
0
                              pTemplate, ulAttributeCount,
478
0
                              pCiphertext, pulCiphertextLen, phKey);
479
0
}
480
481
CK_RV
482
NSC_Decapsulate(CK_SESSION_HANDLE hSession,
483
                CK_MECHANISM_PTR pMechanism,
484
                CK_OBJECT_HANDLE hPrivateKey,
485
                CK_BYTE_PTR pCiphertext,
486
                CK_ULONG ulCiphertextLen,
487
                CK_ATTRIBUTE_PTR pTemplate,
488
                CK_ULONG ulAttributeCount,
489
                /* out */ CK_OBJECT_HANDLE_PTR phKey)
490
0
{
491
0
    return NSC_DecapsulateKey(hSession, pMechanism, hPrivateKey, pTemplate,
492
0
                              ulAttributeCount, pCiphertext, ulCiphertextLen,
493
0
                              phKey);
494
0
}