Coverage Report

Created: 2025-07-01 06:25

/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
KyberParams
15
sftk_kyber_PK11ParamToInternal(CK_NSS_KEM_PARAMETER_SET_TYPE pk11ParamSet)
16
0
{
17
0
    switch (pk11ParamSet) {
18
0
        case CKP_NSS_KYBER_768_ROUND3:
19
0
            return params_kyber768_round3;
20
0
        case CKP_NSS_ML_KEM_768:
21
0
            return params_ml_kem768;
22
0
        default:
23
0
            return params_kyber_invalid;
24
0
    }
25
0
}
26
27
SECItem *
28
sftk_kyber_AllocPubKeyItem(KyberParams params, SECItem *pubkey)
29
0
{
30
0
    switch (params) {
31
0
        case params_kyber768_round3:
32
0
        case params_kyber768_round3_test_mode:
33
0
        case params_ml_kem768:
34
0
        case params_ml_kem768_test_mode:
35
0
            return SECITEM_AllocItem(NULL, pubkey, KYBER768_PUBLIC_KEY_BYTES);
36
0
        default:
37
0
            return NULL;
38
0
    }
39
0
}
40
41
SECItem *
42
sftk_kyber_AllocPrivKeyItem(KyberParams params, SECItem *privkey)
43
0
{
44
0
    switch (params) {
45
0
        case params_kyber768_round3:
46
0
        case params_kyber768_round3_test_mode:
47
0
        case params_ml_kem768:
48
0
        case params_ml_kem768_test_mode:
49
0
            return SECITEM_AllocItem(NULL, privkey, KYBER768_PRIVATE_KEY_BYTES);
50
0
        default:
51
0
            return NULL;
52
0
    }
53
0
}
54
55
SECItem *
56
sftk_kyber_AllocCiphertextItem(KyberParams params, SECItem *ciphertext)
57
0
{
58
0
    switch (params) {
59
0
        case params_kyber768_round3:
60
0
        case params_kyber768_round3_test_mode:
61
0
        case params_ml_kem768:
62
0
        case params_ml_kem768_test_mode:
63
0
            return SECITEM_AllocItem(NULL, ciphertext, KYBER768_CIPHERTEXT_BYTES);
64
0
        default:
65
0
            return NULL;
66
0
    }
67
0
}
68
69
static PRBool
70
sftk_kyber_ValidateParams(const CK_NSS_KEM_PARAMETER_SET_TYPE *params)
71
0
{
72
0
    if (!params) {
73
0
        return PR_FALSE;
74
0
    }
75
0
    switch (*params) {
76
0
        case CKP_NSS_KYBER_768_ROUND3:
77
0
        case CKP_NSS_ML_KEM_768:
78
0
            return PR_TRUE;
79
0
        default:
80
0
            return PR_FALSE;
81
0
    }
82
0
}
83
84
static PRBool
85
sftk_kem_ValidateMechanism(CK_MECHANISM_PTR pMechanism)
86
0
{
87
0
    if (!pMechanism) {
88
0
        return PR_FALSE;
89
0
    }
90
0
    switch (pMechanism->mechanism) {
91
0
        case CKM_NSS_KYBER:
92
0
        case CKM_NSS_ML_KEM:
93
0
            return pMechanism->ulParameterLen == sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE) && sftk_kyber_ValidateParams(pMechanism->pParameter);
94
0
        default:
95
0
            return PR_FALSE;
96
0
    }
97
0
}
98
99
static CK_ULONG
100
sftk_kem_CiphertextLen(CK_MECHANISM_PTR pMechanism)
101
0
{
102
0
#ifdef DEBUG
103
0
    if (!sftk_kem_ValidateMechanism(pMechanism)) {
104
0
        PORT_Assert(0);
105
0
        return 0;
106
0
    }
107
0
#endif
108
109
    /* Assumes pMechanism has been validated with sftk_kem_ValidateMechanism */
110
0
    CK_NSS_KEM_PARAMETER_SET_TYPE *pParameterSet = pMechanism->pParameter;
111
0
    switch (*pParameterSet) {
112
0
        case CKP_NSS_KYBER_768_ROUND3:
113
0
        case CKP_NSS_ML_KEM_768:
114
0
            return KYBER768_CIPHERTEXT_BYTES;
115
0
        default:
116
            /* unreachable if pMechanism has been validated */
117
0
            PORT_Assert(0);
118
0
            return 0;
119
0
    }
120
0
}
121
122
/* C_Encapsulate takes a public encapsulation key hPublicKey, a secret
123
 * phKey, and outputs a ciphertext (i.e. encapsulaton) of this secret in
124
 * pCiphertext. */
125
CK_RV
126
NSC_Encapsulate(CK_SESSION_HANDLE hSession,
127
                CK_MECHANISM_PTR pMechanism,
128
                CK_OBJECT_HANDLE hPublicKey,
129
                CK_ATTRIBUTE_PTR pTemplate,
130
                CK_ULONG ulAttributeCount,
131
                /* out */ CK_OBJECT_HANDLE_PTR phKey,
132
                /* out */ CK_BYTE_PTR pCiphertext,
133
                /* out */ CK_ULONG_PTR pulCiphertextLen)
134
0
{
135
0
    SFTKSession *session = NULL;
136
0
    SFTKSlot *slot = NULL;
137
138
0
    SFTKObject *key = NULL;
139
140
0
    SFTKObject *encapsulationKeyObject = NULL;
141
0
    SFTKAttribute *encapsulationKey = NULL;
142
143
0
    CK_RV crv;
144
0
    SFTKFreeStatus status;
145
146
0
    CHECK_FORK();
147
148
0
    if (!pMechanism || !phKey || !pulCiphertextLen) {
149
0
        return CKR_ARGUMENTS_BAD;
150
0
    }
151
152
0
    if (!sftk_kem_ValidateMechanism(pMechanism)) {
153
0
        return CKR_MECHANISM_INVALID;
154
0
    }
155
156
0
    CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism);
157
0
    if (!pCiphertext || *pulCiphertextLen < ciphertextLen) {
158
0
        *pulCiphertextLen = ciphertextLen;
159
0
        return CKR_KEY_SIZE_RANGE;
160
0
    }
161
0
    *phKey = CK_INVALID_HANDLE;
162
163
0
    session = sftk_SessionFromHandle(hSession);
164
0
    if (session == NULL) {
165
0
        return CKR_SESSION_HANDLE_INVALID;
166
0
    }
167
0
    slot = sftk_SlotFromSessionHandle(hSession);
168
0
    if (slot == NULL) {
169
0
        crv = CKR_SESSION_HANDLE_INVALID;
170
0
        goto cleanup;
171
0
    }
172
173
0
    key = sftk_NewObject(slot);
174
0
    if (key == NULL) {
175
0
        crv = CKR_HOST_MEMORY;
176
0
        goto cleanup;
177
0
    }
178
0
    for (unsigned long int i = 0; i < ulAttributeCount; i++) {
179
0
        crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
180
0
        if (crv != CKR_OK) {
181
0
            goto cleanup;
182
0
        }
183
0
    }
184
185
0
    encapsulationKeyObject = sftk_ObjectFromHandle(hPublicKey, session);
186
0
    if (encapsulationKeyObject == NULL) {
187
0
        crv = CKR_KEY_HANDLE_INVALID;
188
0
        goto cleanup;
189
0
    }
190
0
    encapsulationKey = sftk_FindAttribute(encapsulationKeyObject, CKA_VALUE);
191
0
    if (encapsulationKey == NULL) {
192
0
        crv = CKR_KEY_HANDLE_INVALID;
193
0
        goto cleanup;
194
0
    }
195
196
0
    SECItem ciphertext = { siBuffer, pCiphertext, ciphertextLen };
197
0
    SECItem pubKey = { siBuffer, encapsulationKey->attrib.pValue, encapsulationKey->attrib.ulValueLen };
198
199
    /* The length of secretBuf can be increased if we ever support other KEMs */
200
0
    uint8_t secretBuf[KYBER_SHARED_SECRET_BYTES] = { 0 };
201
0
    SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
202
203
0
    switch (pMechanism->mechanism) {
204
0
        case CKM_NSS_KYBER:
205
0
        case CKM_NSS_ML_KEM:
206
0
            PORT_Assert(secret.len == KYBER_SHARED_SECRET_BYTES);
207
0
            CK_NSS_KEM_PARAMETER_SET_TYPE *pParameter = pMechanism->pParameter;
208
0
            KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(*pParameter);
209
0
            SECStatus rv = Kyber_Encapsulate(kyberParams, /* seed */ NULL, &pubKey, &ciphertext, &secret);
210
0
            if (rv != SECSuccess) {
211
0
                crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
212
0
                goto cleanup;
213
0
            }
214
215
0
            crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
216
0
            if (crv != CKR_OK) {
217
0
                goto cleanup;
218
0
            }
219
220
0
            crv = sftk_handleObject(key, session);
221
0
            if (crv != CKR_OK) {
222
0
                goto cleanup;
223
0
            }
224
225
            /* We wrote the ciphertext out directly in Kyber_Encapsulate */
226
0
            *phKey = key->handle;
227
0
            *pulCiphertextLen = ciphertext.len;
228
0
            break;
229
0
        default:
230
0
            crv = CKR_MECHANISM_INVALID;
231
0
            goto cleanup;
232
0
    }
233
234
0
cleanup:
235
0
    if (session) {
236
0
        sftk_FreeSession(session);
237
0
    }
238
0
    if (key) {
239
0
        status = sftk_FreeObject(key);
240
0
        if (status == SFTK_DestroyFailure) {
241
0
            return CKR_DEVICE_ERROR;
242
0
        }
243
0
    }
244
0
    if (encapsulationKeyObject) {
245
0
        status = sftk_FreeObject(encapsulationKeyObject);
246
0
        if (status == SFTK_DestroyFailure) {
247
0
            return CKR_DEVICE_ERROR;
248
0
        }
249
0
    }
250
0
    if (encapsulationKey) {
251
0
        sftk_FreeAttribute(encapsulationKey);
252
0
    }
253
0
    return crv;
254
0
}
255
256
CK_RV
257
NSC_Decapsulate(CK_SESSION_HANDLE hSession,
258
                CK_MECHANISM_PTR pMechanism,
259
                CK_OBJECT_HANDLE hPrivateKey,
260
                CK_BYTE_PTR pCiphertext,
261
                CK_ULONG ulCiphertextLen,
262
                CK_ATTRIBUTE_PTR pTemplate,
263
                CK_ULONG ulAttributeCount,
264
                /* out */ CK_OBJECT_HANDLE_PTR phKey)
265
0
{
266
0
    SFTKSession *session = NULL;
267
0
    SFTKSlot *slot = NULL;
268
269
0
    SFTKObject *key = NULL;
270
271
0
    SFTKObject *decapsulationKeyObject = NULL;
272
0
    SFTKAttribute *decapsulationKey = NULL;
273
274
0
    CK_RV crv;
275
0
    SFTKFreeStatus status;
276
277
0
    CHECK_FORK();
278
279
0
    if (!pMechanism || !pCiphertext || !pTemplate || !phKey) {
280
0
        return CKR_ARGUMENTS_BAD;
281
0
    }
282
283
0
    if (!sftk_kem_ValidateMechanism(pMechanism)) {
284
0
        return CKR_MECHANISM_INVALID;
285
0
    }
286
287
0
    CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism);
288
0
    if (ulCiphertextLen < ciphertextLen) {
289
0
        return CKR_ARGUMENTS_BAD;
290
0
    }
291
0
    *phKey = CK_INVALID_HANDLE;
292
293
0
    session = sftk_SessionFromHandle(hSession);
294
0
    if (session == NULL) {
295
0
        return CKR_SESSION_HANDLE_INVALID;
296
0
    }
297
0
    slot = sftk_SlotFromSessionHandle(hSession);
298
0
    if (slot == NULL) {
299
0
        crv = CKR_SESSION_HANDLE_INVALID;
300
0
        goto cleanup;
301
0
    }
302
303
0
    key = sftk_NewObject(slot);
304
0
    if (key == NULL) {
305
0
        crv = CKR_HOST_MEMORY;
306
0
        goto cleanup;
307
0
    }
308
0
    for (unsigned long int i = 0; i < ulAttributeCount; i++) {
309
0
        crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i]));
310
0
        if (crv != CKR_OK) {
311
0
            goto cleanup;
312
0
        }
313
0
    }
314
315
0
    decapsulationKeyObject = sftk_ObjectFromHandle(hPrivateKey, session);
316
0
    if (decapsulationKeyObject == NULL) {
317
0
        crv = CKR_KEY_HANDLE_INVALID;
318
0
        goto cleanup;
319
0
    }
320
0
    decapsulationKey = sftk_FindAttribute(decapsulationKeyObject, CKA_VALUE);
321
0
    if (decapsulationKey == NULL) {
322
0
        crv = CKR_KEY_HANDLE_INVALID;
323
0
        goto cleanup;
324
0
    }
325
326
0
    SECItem privKey = { siBuffer, decapsulationKey->attrib.pValue, decapsulationKey->attrib.ulValueLen };
327
0
    SECItem ciphertext = { siBuffer, pCiphertext, ulCiphertextLen };
328
329
    /* The length of secretBuf can be increased if we ever support other KEMs */
330
0
    uint8_t secretBuf[KYBER_SHARED_SECRET_BYTES] = { 0 };
331
0
    SECItem secret = { siBuffer, secretBuf, sizeof secretBuf };
332
333
0
    switch (pMechanism->mechanism) {
334
0
        case CKM_NSS_KYBER:
335
0
        case CKM_NSS_ML_KEM:
336
0
            PORT_Assert(secret.len == KYBER_SHARED_SECRET_BYTES);
337
0
            CK_NSS_KEM_PARAMETER_SET_TYPE *pParameter = pMechanism->pParameter;
338
0
            KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(*pParameter);
339
0
            SECStatus rv = Kyber_Decapsulate(kyberParams, &privKey, &ciphertext, &secret);
340
0
            if (rv != SECSuccess) {
341
0
                crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED;
342
0
                goto cleanup;
343
0
            }
344
345
0
            crv = sftk_forceAttribute(key, CKA_VALUE, sftk_item_expand(&secret));
346
0
            if (crv != CKR_OK) {
347
0
                goto cleanup;
348
0
            }
349
350
0
            crv = sftk_handleObject(key, session);
351
0
            if (crv != CKR_OK) {
352
0
                goto cleanup;
353
0
            }
354
0
            *phKey = key->handle;
355
0
            break;
356
0
        default:
357
0
            crv = CKR_MECHANISM_INVALID;
358
0
            goto cleanup;
359
0
    }
360
361
0
cleanup:
362
0
    if (session) {
363
0
        sftk_FreeSession(session);
364
0
    }
365
0
    if (key) {
366
0
        status = sftk_FreeObject(key);
367
0
        if (status == SFTK_DestroyFailure) {
368
0
            return CKR_DEVICE_ERROR;
369
0
        }
370
0
    }
371
0
    if (decapsulationKeyObject) {
372
0
        status = sftk_FreeObject(decapsulationKeyObject);
373
0
        if (status == SFTK_DestroyFailure) {
374
0
            return CKR_DEVICE_ERROR;
375
0
        }
376
0
    }
377
0
    if (decapsulationKey) {
378
0
        sftk_FreeAttribute(decapsulationKey);
379
0
    }
380
0
    return crv;
381
0
}