Coverage Report

Created: 2025-07-27 06:08

/src/nss/lib/softoken/kem.c
Line
Count
Source (jump to first uncovered line)
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
0
{
20
0
    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
0
        case CKP_NSS_ML_KEM_768:
26
0
        case CKP_ML_KEM_768:
27
0
            return params_ml_kem768;
28
0
        default:
29
0
            return params_kyber_invalid;
30
0
    }
31
0
}
32
33
SECItem *
34
sftk_kyber_AllocPubKeyItem(KyberParams params, SECItem *pubkey)
35
0
{
36
0
    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
0
        case params_ml_kem768:
42
0
        case params_ml_kem768_test_mode:
43
0
            return SECITEM_AllocItem(NULL, pubkey, KYBER768_PUBLIC_KEY_BYTES);
44
0
        default:
45
0
            return NULL;
46
0
    }
47
0
}
48
49
SECItem *
50
sftk_kyber_AllocPrivKeyItem(KyberParams params, SECItem *privkey)
51
0
{
52
0
    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
0
        case params_ml_kem768:
58
0
        case params_ml_kem768_test_mode:
59
0
            return SECITEM_AllocItem(NULL, privkey, KYBER768_PRIVATE_KEY_BYTES);
60
0
        default:
61
0
            return NULL;
62
0
    }
63
0
}
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
0
{
84
0
    if (!pMechanism) {
85
0
        return PR_FALSE;
86
0
    }
87
0
    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
0
        case CKM_ML_KEM:
93
0
            return PR_TRUE;
94
0
        default:
95
0
            return PR_FALSE;
96
0
    }
97
0
}
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
0
{
107
0
    CK_RV crv = CKR_MECHANISM_INVALID;
108
109
0
    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
0
            if ((pMechanism->pParameter) &&
115
0
                pMechanism->ulParameterLen == sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)) {
116
0
                PR_STATIC_ASSERT(sizeof(CK_ML_KEM_PARAMETER_SET_TYPE) == sizeof(CK_LONG));
117
0
                *paramSet = *(CK_ULONG *)pMechanism->pParameter;
118
0
                crv = CKR_OK;
119
0
                break;
120
0
            }
121
0
            crv = sftk_GetULongAttribute(key, CKA_NSS_PARAMETER_SET, paramSet);
122
0
            break;
123
0
        case CKM_ML_KEM:
124
0
            crv = sftk_GetULongAttribute(key, CKA_PARAMETER_SET, paramSet);
125
0
            break;
126
0
        default:
127
0
            break;
128
0
    }
129
0
    return crv;
130
0
}
131
132
static CK_ULONG
133
sftk_kem_CiphertextLen(CK_MECHANISM_PTR pMechanism, CK_ULONG paramSet)
134
0
{
135
    /* the switch here is redundant now, but we will eventually have
136
     * other unrelated KEM operations and the meaning of paramSet
137
     * is dependent of the mechanism type (in general). Since there
138
     * is not overlap between the Vendor specific mechanisms here
139
     * and the stand ones, we'll just accept them all here. */
140
0
    switch (pMechanism->mechanism) {
141
0
#ifndef NSS_DISABLE_KYBER
142
0
        case CKM_NSS_KYBER:
143
0
#endif
144
0
        case CKM_NSS_ML_KEM:
145
0
        case CKM_ML_KEM:
146
0
            switch (paramSet) {
147
0
#ifndef NSS_DISABLE_KYBER
148
0
                case CKP_NSS_KYBER_768_ROUND3:
149
0
#endif
150
0
                case CKP_NSS_ML_KEM_768:
151
0
                case CKP_ML_KEM_768:
152
0
                    return KYBER768_CIPHERTEXT_BYTES;
153
0
                default:
154
0
                    break;
155
0
            }
156
0
        default:
157
0
            break;
158
0
    }
159
0
    return 0;
160
0
}
161
162
/* C_Encapsulate takes a public encapsulation key hPublicKey, a secret
163
 * phKey, and outputs a ciphertext (i.e. encapsulaton) of this secret in
164
 * pCiphertext. */
165
CK_RV
166
NSC_EncapsulateKey(CK_SESSION_HANDLE hSession,
167
                   CK_MECHANISM_PTR pMechanism,
168
                   CK_OBJECT_HANDLE hPublicKey,
169
                   CK_ATTRIBUTE_PTR pTemplate,
170
                   CK_ULONG ulAttributeCount,
171
                   /* out */ CK_BYTE_PTR pCiphertext,
172
                   /* out */ CK_ULONG_PTR pulCiphertextLen,
173
                   /* out */ CK_OBJECT_HANDLE_PTR phKey)
174
0
{
175
0
    SFTKSession *session = NULL;
176
0
    SFTKSlot *slot = NULL;
177
178
0
    SFTKObject *key = NULL;
179
180
0
    SFTKObject *encapsulationKeyObject = NULL;
181
0
    SFTKAttribute *encapsulationKey = NULL;
182
183
0
    CK_RV crv;
184
0
    SFTKFreeStatus status;
185
0
    CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle
186
                            * different param set types based on the
187
                            * Mechanism value */
188
0
    KyberParams kyberParams;
189
190
0
    CHECK_FORK();
191
192
0
    if (!pMechanism || !phKey || !pulCiphertextLen) {
193
0
        return CKR_ARGUMENTS_BAD;
194
0
    }
195
196
0
    if (!sftk_kem_ValidateMechanism(pMechanism)) {
197
0
        return CKR_MECHANISM_INVALID;
198
0
    }
199
0
    *phKey = CK_INVALID_HANDLE;
200
201
0
    session = sftk_SessionFromHandle(hSession);
202
0
    if (session == NULL) {
203
0
        return CKR_SESSION_HANDLE_INVALID;
204
0
    }
205
0
    slot = sftk_SlotFromSessionHandle(hSession);
206
0
    if (slot == NULL) {
207
0
        crv = CKR_SESSION_HANDLE_INVALID;
208
0
        goto cleanup;
209
0
    }
210
211
0
    key = sftk_NewObject(slot);
212
0
    if (key == NULL) {
213
0
        crv = CKR_HOST_MEMORY;
214
0
        goto cleanup;
215
0
    }
216
0
    for (unsigned long int i = 0; i < ulAttributeCount; i++) {
217
0
        crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
218
0
        if (crv != CKR_OK) {
219
0
            goto cleanup;
220
0
        }
221
0
    }
222
223
0
    encapsulationKeyObject = sftk_ObjectFromHandle(hPublicKey, session);
224
0
    if (encapsulationKeyObject == NULL) {
225
0
        crv = CKR_KEY_HANDLE_INVALID;
226
0
        goto cleanup;
227
0
    }
228
0
    encapsulationKey = sftk_FindAttribute(encapsulationKeyObject, CKA_VALUE);
229
0
    if (encapsulationKey == NULL) {
230
0
        crv = CKR_KEY_HANDLE_INVALID;
231
0
        goto cleanup;
232
0
    }
233
234
0
    crv = sftk_kem_getParamSet(pMechanism, encapsulationKeyObject, &paramSet);
235
0
    if (crv != CKR_OK) {
236
0
        goto cleanup;
237
0
    }
238
239
0
    CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism, paramSet);
240
0
    if (!pCiphertext || *pulCiphertextLen < ciphertextLen || ciphertextLen == 0) {
241
0
        *pulCiphertextLen = ciphertextLen;
242
0
        crv = CKR_KEY_SIZE_RANGE;
243
0
        goto cleanup;
244
0
    }
245
246
0
    SECItem ciphertext = { siBuffer, pCiphertext, ciphertextLen };
247
0
    SECItem pubKey = { siBuffer, encapsulationKey->attrib.pValue, encapsulationKey->attrib.ulValueLen };
248
249
    /* The length of secretBuf can be increased if we ever support other KEMs
250
     * by changing the define at the top of this file */
251
0
    uint8_t secretBuf[MAX_SHARED_SECRET_BYTES] = { 0 };
252
0
    SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
253
254
0
    switch (pMechanism->mechanism) {
255
0
#ifndef NSS_DISABLE_KYBER
256
0
        case CKM_NSS_KYBER:
257
0
#endif
258
0
        case CKM_NSS_ML_KEM:
259
0
        case CKM_ML_KEM:
260
0
            PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES);
261
0
            kyberParams = sftk_kyber_PK11ParamToInternal(paramSet);
262
0
            SECStatus rv = Kyber_Encapsulate(kyberParams, /* seed */ NULL,
263
0
                                             &pubKey, &ciphertext, &secret);
264
0
            if (rv != SECSuccess) {
265
0
                crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
266
0
                goto cleanup;
267
0
            }
268
269
0
            crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
270
0
            if (crv != CKR_OK) {
271
0
                goto cleanup;
272
0
            }
273
274
0
            crv = sftk_handleObject(key, session);
275
0
            if (crv != CKR_OK) {
276
0
                goto cleanup;
277
0
            }
278
279
            /* We wrote the ciphertext out directly in Kyber_Encapsulate */
280
0
            *phKey = key->handle;
281
0
            *pulCiphertextLen = ciphertext.len;
282
0
            break;
283
0
        default:
284
0
            crv = CKR_MECHANISM_INVALID;
285
0
            goto cleanup;
286
0
    }
287
288
0
cleanup:
289
0
    if (session) {
290
0
        sftk_FreeSession(session);
291
0
    }
292
0
    if (key) {
293
0
        status = sftk_FreeObject(key);
294
0
        if (status == SFTK_DestroyFailure) {
295
0
            return CKR_DEVICE_ERROR;
296
0
        }
297
0
    }
298
0
    if (encapsulationKeyObject) {
299
0
        status = sftk_FreeObject(encapsulationKeyObject);
300
0
        if (status == SFTK_DestroyFailure) {
301
0
            return CKR_DEVICE_ERROR;
302
0
        }
303
0
    }
304
0
    if (encapsulationKey) {
305
0
        sftk_FreeAttribute(encapsulationKey);
306
0
    }
307
0
    return crv;
308
0
}
309
310
CK_RV
311
NSC_DecapsulateKey(CK_SESSION_HANDLE hSession,
312
                   CK_MECHANISM_PTR pMechanism,
313
                   CK_OBJECT_HANDLE hPrivateKey,
314
                   CK_ATTRIBUTE_PTR pTemplate,
315
                   CK_ULONG ulAttributeCount,
316
                   CK_BYTE_PTR pCiphertext,
317
                   CK_ULONG ulCiphertextLen,
318
                   /* out */ CK_OBJECT_HANDLE_PTR phKey)
319
0
{
320
0
    SFTKSession *session = NULL;
321
0
    SFTKSlot *slot = NULL;
322
323
0
    SFTKObject *key = NULL;
324
325
0
    SFTKObject *decapsulationKeyObject = NULL;
326
0
    SFTKAttribute *decapsulationKey = NULL;
327
0
    CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle
328
                            * different param set types based on the
329
                            * Mechanism value */
330
331
0
    CK_RV crv;
332
0
    SFTKFreeStatus status;
333
0
    KyberParams kyberParams;
334
335
0
    CHECK_FORK();
336
337
0
    if (!pMechanism || !pCiphertext || !pTemplate || !phKey) {
338
0
        return CKR_ARGUMENTS_BAD;
339
0
    }
340
341
0
    if (!sftk_kem_ValidateMechanism(pMechanism)) {
342
0
        return CKR_MECHANISM_INVALID;
343
0
    }
344
0
    *phKey = CK_INVALID_HANDLE;
345
346
0
    session = sftk_SessionFromHandle(hSession);
347
0
    if (session == NULL) {
348
0
        return CKR_SESSION_HANDLE_INVALID;
349
0
    }
350
0
    slot = sftk_SlotFromSessionHandle(hSession);
351
0
    if (slot == NULL) {
352
0
        crv = CKR_SESSION_HANDLE_INVALID;
353
0
        goto cleanup;
354
0
    }
355
356
0
    key = sftk_NewObject(slot);
357
0
    if (key == NULL) {
358
0
        crv = CKR_HOST_MEMORY;
359
0
        goto cleanup;
360
0
    }
361
0
    for (unsigned long int i = 0; i < ulAttributeCount; i++) {
362
0
        crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
363
0
        if (crv != CKR_OK) {
364
0
            goto cleanup;
365
0
        }
366
0
    }
367
368
0
    decapsulationKeyObject = sftk_ObjectFromHandle(hPrivateKey, session);
369
0
    if (decapsulationKeyObject == NULL) {
370
0
        crv = CKR_KEY_HANDLE_INVALID;
371
0
        goto cleanup;
372
0
    }
373
0
    decapsulationKey = sftk_FindAttribute(decapsulationKeyObject, CKA_VALUE);
374
0
    if (decapsulationKey == NULL) {
375
0
        crv = CKR_KEY_HANDLE_INVALID;
376
0
        goto cleanup;
377
0
    }
378
379
0
    crv = sftk_kem_getParamSet(pMechanism, decapsulationKeyObject, &paramSet);
380
0
    if (crv != CKR_OK) {
381
0
        goto cleanup;
382
0
    }
383
384
0
    CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism, paramSet);
385
0
    if (!pCiphertext || ulCiphertextLen != ciphertextLen || ciphertextLen == 0) {
386
0
        crv = CKR_ARGUMENTS_BAD;
387
0
        goto cleanup;
388
0
    }
389
390
0
    SECItem privKey = { siBuffer, decapsulationKey->attrib.pValue,
391
0
                        decapsulationKey->attrib.ulValueLen };
392
0
    SECItem ciphertext = { siBuffer, pCiphertext, ulCiphertextLen };
393
394
    /* The length of secretBuf can be increased if we ever support other KEMs
395
     * by changing the define at the top of this file */
396
0
    uint8_t secretBuf[MAX_SHARED_SECRET_BYTES] = { 0 };
397
0
    SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
398
399
0
    switch (pMechanism->mechanism) {
400
0
#ifndef NSS_DISABLE_KYBER
401
0
        case CKM_NSS_KYBER:
402
0
#endif
403
0
        case CKM_NSS_ML_KEM:
404
0
        case CKM_ML_KEM:
405
0
            kyberParams = sftk_kyber_PK11ParamToInternal(paramSet);
406
0
            PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES);
407
0
            SECStatus rv = Kyber_Decapsulate(kyberParams, &privKey,
408
0
                                             &ciphertext, &secret);
409
0
            if (rv != SECSuccess) {
410
0
                crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
411
0
                goto cleanup;
412
0
            }
413
414
0
            crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
415
0
            if (crv != CKR_OK) {
416
0
                goto cleanup;
417
0
            }
418
419
0
            crv = sftk_handleObject(key, session);
420
0
            if (crv != CKR_OK) {
421
0
                goto cleanup;
422
0
            }
423
0
            *phKey = key->handle;
424
0
            break;
425
0
        default:
426
0
            crv = CKR_MECHANISM_INVALID;
427
0
            goto cleanup;
428
0
    }
429
430
0
cleanup:
431
0
    if (session) {
432
0
        sftk_FreeSession(session);
433
0
    }
434
0
    if (key) {
435
0
        status = sftk_FreeObject(key);
436
0
        if (status == SFTK_DestroyFailure) {
437
0
            return CKR_DEVICE_ERROR;
438
0
        }
439
0
    }
440
0
    if (decapsulationKeyObject) {
441
0
        status = sftk_FreeObject(decapsulationKeyObject);
442
0
        if (status == SFTK_DestroyFailure) {
443
0
            return CKR_DEVICE_ERROR;
444
0
        }
445
0
    }
446
0
    if (decapsulationKey) {
447
0
        sftk_FreeAttribute(decapsulationKey);
448
0
    }
449
0
    return crv;
450
0
}
451
452
/* PKCS #11 final spec moved som the the arguments around (to make
453
 * NSC_EncapsulateKey and NSC_DecapsulateKey match, keep the old version for
454
 * the vendor defined for backward compatibility */
455
CK_RV
456
NSC_Encapsulate(CK_SESSION_HANDLE hSession,
457
                CK_MECHANISM_PTR pMechanism,
458
                CK_OBJECT_HANDLE hPublicKey,
459
                CK_ATTRIBUTE_PTR pTemplate,
460
                CK_ULONG ulAttributeCount,
461
                /* out */ CK_OBJECT_HANDLE_PTR phKey,
462
                /* out */ CK_BYTE_PTR pCiphertext,
463
                /* out */ CK_ULONG_PTR pulCiphertextLen)
464
0
{
465
0
    return NSC_EncapsulateKey(hSession, pMechanism, hPublicKey,
466
0
                              pTemplate, ulAttributeCount,
467
0
                              pCiphertext, pulCiphertextLen, phKey);
468
0
}
469
470
CK_RV
471
NSC_Decapsulate(CK_SESSION_HANDLE hSession,
472
                CK_MECHANISM_PTR pMechanism,
473
                CK_OBJECT_HANDLE hPrivateKey,
474
                CK_BYTE_PTR pCiphertext,
475
                CK_ULONG ulCiphertextLen,
476
                CK_ATTRIBUTE_PTR pTemplate,
477
                CK_ULONG ulAttributeCount,
478
                /* out */ CK_OBJECT_HANDLE_PTR phKey)
479
0
{
480
0
    return NSC_DecapsulateKey(hSession, pMechanism, hPrivateKey, pTemplate,
481
0
                              ulAttributeCount, pCiphertext, ulCiphertextLen,
482
0
                              phKey);
483
0
}