Coverage Report

Created: 2025-06-24 06:49

/src/nss/lib/freebl/kyber.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
#ifdef FREEBL_NO_DEPEND
6
#include "stubs.h"
7
#endif
8
9
#include <stdbool.h>
10
11
#include "blapi.h"
12
#include "secerr.h"
13
#include "secitem.h"
14
15
#include "kyber-pqcrystals-ref.h"
16
#include "kyber.h"
17
#include "verified/internal/libcrux_core.h"
18
#include "verified/libcrux_mlkem768_portable.h"
19
#include "verified/libcrux_mlkem768.h"
20
21
/* Consistency check between kyber-pqcrystals-ref.h and kyber.h */
22
PR_STATIC_ASSERT(KYBER768_PUBLIC_KEY_BYTES == pqcrystals_kyber768_PUBLICKEYBYTES);
23
PR_STATIC_ASSERT(KYBER768_PRIVATE_KEY_BYTES == pqcrystals_kyber768_SECRETKEYBYTES);
24
PR_STATIC_ASSERT(KYBER768_CIPHERTEXT_BYTES == pqcrystals_kyber768_CIPHERTEXTBYTES);
25
PR_STATIC_ASSERT(KYBER_SHARED_SECRET_BYTES == pqcrystals_kyber768_BYTES);
26
PR_STATIC_ASSERT(KYBER_KEYPAIR_COIN_BYTES == pqcrystals_kyber768_KEYPAIRCOINBYTES);
27
PR_STATIC_ASSERT(KYBER_ENC_COIN_BYTES == pqcrystals_kyber768_ENCCOINBYTES);
28
29
/* Consistency check between libcrux_mlkem768_portable.h and kyber.h */
30
PR_STATIC_ASSERT(KYBER768_PUBLIC_KEY_BYTES == LIBCRUX_ML_KEM_MLKEM768_CPA_PKE_PUBLIC_KEY_SIZE_768);
31
PR_STATIC_ASSERT(KYBER768_PRIVATE_KEY_BYTES == LIBCRUX_ML_KEM_MLKEM768_SECRET_KEY_SIZE_768);
32
PR_STATIC_ASSERT(KYBER768_CIPHERTEXT_BYTES == LIBCRUX_ML_KEM_MLKEM768_CPA_PKE_CIPHERTEXT_SIZE_768);
33
PR_STATIC_ASSERT(KYBER_SHARED_SECRET_BYTES == LIBCRUX_ML_KEM_CONSTANTS_SHARED_SECRET_SIZE);
34
PR_STATIC_ASSERT(KYBER_KEYPAIR_COIN_BYTES == 64);
35
PR_STATIC_ASSERT(KYBER_ENC_COIN_BYTES == 32);
36
37
static bool
38
valid_params(KyberParams params)
39
287
{
40
287
    switch (params) {
41
0
        case params_kyber768_round3:
42
0
        case params_kyber768_round3_test_mode:
43
287
        case params_ml_kem768:
44
287
        case params_ml_kem768_test_mode:
45
287
            return true;
46
0
        default:
47
0
            return false;
48
287
    }
49
287
}
50
51
static bool
52
valid_pubkey(KyberParams params, const SECItem *pubkey)
53
276
{
54
276
    switch (params) {
55
0
        case params_kyber768_round3:
56
0
        case params_kyber768_round3_test_mode:
57
276
        case params_ml_kem768:
58
276
        case params_ml_kem768_test_mode:
59
276
            return pubkey && pubkey->len == KYBER768_PUBLIC_KEY_BYTES;
60
0
        default:
61
0
            return false;
62
276
    }
63
276
}
64
65
static bool
66
valid_privkey(KyberParams params, const SECItem *privkey)
67
104
{
68
104
    switch (params) {
69
0
        case params_kyber768_round3:
70
0
        case params_kyber768_round3_test_mode:
71
104
        case params_ml_kem768:
72
104
        case params_ml_kem768_test_mode:
73
104
            return privkey && privkey->len == KYBER768_PRIVATE_KEY_BYTES;
74
0
        default:
75
0
            return false;
76
104
    }
77
104
}
78
79
static bool
80
valid_ciphertext(KyberParams params, const SECItem *ciphertext)
81
194
{
82
194
    switch (params) {
83
0
        case params_kyber768_round3:
84
0
        case params_kyber768_round3_test_mode:
85
194
        case params_ml_kem768:
86
194
        case params_ml_kem768_test_mode:
87
194
            return ciphertext && ciphertext->len == KYBER768_CIPHERTEXT_BYTES;
88
0
        default:
89
0
            return false;
90
194
    }
91
194
}
92
93
static bool
94
valid_secret(KyberParams params, const SECItem *secret)
95
194
{
96
194
    switch (params) {
97
0
        case params_kyber768_round3:
98
0
        case params_kyber768_round3_test_mode:
99
194
        case params_ml_kem768:
100
194
        case params_ml_kem768_test_mode:
101
194
            return secret && secret->len == KYBER_SHARED_SECRET_BYTES;
102
0
        default:
103
0
            return false;
104
194
    }
105
194
}
106
107
static bool
108
valid_keypair_seed(KyberParams params, const SECItem *seed)
109
93
{
110
93
    switch (params) {
111
0
        case params_kyber768_round3:
112
0
        case params_kyber768_round3_test_mode:
113
93
        case params_ml_kem768:
114
93
        case params_ml_kem768_test_mode:
115
93
            return !seed || seed->len == KYBER_KEYPAIR_COIN_BYTES;
116
0
        default:
117
0
            return false;
118
93
    }
119
93
}
120
121
static bool
122
valid_enc_seed(KyberParams params, const SECItem *seed)
123
183
{
124
183
    switch (params) {
125
0
        case params_kyber768_round3:
126
183
        case params_ml_kem768:
127
183
            return !seed;
128
0
        case params_kyber768_round3_test_mode:
129
0
        case params_ml_kem768_test_mode:
130
0
            return !seed || seed->len == KYBER_SHARED_SECRET_BYTES;
131
0
        default:
132
0
            return false;
133
183
    }
134
183
}
135
136
SECStatus
137
Kyber_NewKey(KyberParams params, const SECItem *keypair_seed, SECItem *privkey, SECItem *pubkey)
138
93
{
139
93
    if (!valid_params(params)) {
140
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
141
0
        return SECFailure;
142
0
    }
143
144
93
    if (!(valid_keypair_seed(params, keypair_seed) && valid_privkey(params, privkey) && valid_pubkey(params, pubkey))) {
145
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
146
0
        return SECFailure;
147
0
    }
148
149
93
    uint8_t randbuf[KYBER_KEYPAIR_COIN_BYTES];
150
93
    uint8_t *coins;
151
93
    if (keypair_seed) {
152
0
        coins = keypair_seed->data;
153
93
    } else {
154
93
        if (RNG_GenerateGlobalRandomBytes(randbuf, sizeof randbuf) != SECSuccess) {
155
0
            PORT_SetError(SEC_ERROR_NEED_RANDOM);
156
0
            return SECFailure;
157
0
        }
158
93
        coins = randbuf;
159
93
    }
160
93
    NSS_CLASSIFY(coins, KYBER_KEYPAIR_COIN_BYTES);
161
93
    if (params == params_kyber768_round3 || params == params_kyber768_round3_test_mode) {
162
0
        pqcrystals_kyber768_ref_keypair_derand(pubkey->data, privkey->data, coins);
163
93
    } else if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) {
164
93
        libcrux_ml_kem_mlkem768_MlKem768KeyPair keys = libcrux_ml_kem_mlkem768_portable_generate_key_pair(coins);
165
93
        memcpy(pubkey->data, keys.pk.value, KYBER768_PUBLIC_KEY_BYTES);
166
93
        memcpy(privkey->data, keys.sk.value, KYBER768_PRIVATE_KEY_BYTES);
167
93
    } else {
168
        /* unreachable */
169
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
170
0
        return SECFailure;
171
0
    }
172
93
    NSS_DECLASSIFY(pubkey->data, pubkey->len);
173
93
    return SECSuccess;
174
93
}
175
176
SECStatus
177
Kyber_Encapsulate(KyberParams params, const SECItem *enc_seed, const SECItem *pubkey, SECItem *ciphertext, SECItem *secret)
178
183
{
179
183
    if (!valid_params(params)) {
180
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
181
0
        return SECFailure;
182
0
    }
183
184
183
    if (!(valid_enc_seed(params, enc_seed) && valid_pubkey(params, pubkey) && valid_ciphertext(params, ciphertext) && valid_secret(params, secret))) {
185
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
186
0
        return SECFailure;
187
0
    }
188
189
183
    uint8_t randbuf[KYBER_ENC_COIN_BYTES];
190
183
    uint8_t *coins;
191
183
    if (enc_seed) {
192
0
        coins = enc_seed->data;
193
183
    } else {
194
183
        if (RNG_GenerateGlobalRandomBytes(randbuf, sizeof randbuf) != SECSuccess) {
195
0
            PORT_SetError(SEC_ERROR_NEED_RANDOM);
196
0
            return SECFailure;
197
0
        }
198
183
        coins = randbuf;
199
183
    }
200
183
    NSS_CLASSIFY(coins, KYBER_ENC_COIN_BYTES);
201
183
    if (params == params_kyber768_round3 || params == params_kyber768_round3_test_mode) {
202
0
        pqcrystals_kyber768_ref_enc_derand(ciphertext->data, secret->data, pubkey->data, coins);
203
183
    } else if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) {
204
183
        libcrux_ml_kem_types_MlKemPublicKey_15 pk_value;
205
183
        memcpy(pk_value.value, pubkey->data, KYBER768_PUBLIC_KEY_BYTES);
206
207
183
        bool valid_pk = libcrux_ml_kem_mlkem768_portable_validate_public_key(&pk_value);
208
183
        if (!valid_pk) {
209
32
            PORT_SetError(SEC_ERROR_INVALID_ARGS);
210
32
            return SECFailure;
211
32
        }
212
213
151
        tuple_3c encap = libcrux_ml_kem_mlkem768_portable_encapsulate(&pk_value, coins);
214
151
        memcpy(ciphertext->data, encap.fst.value, KYBER768_CIPHERTEXT_BYTES);
215
151
        memcpy(secret->data, encap.snd, KYBER_SHARED_SECRET_BYTES);
216
151
    } else {
217
        /* unreachable */
218
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
219
0
        return SECFailure;
220
0
    }
221
222
151
    return SECSuccess;
223
183
}
224
225
SECStatus
226
Kyber_Decapsulate(KyberParams params, const SECItem *privkey, const SECItem *ciphertext, SECItem *secret)
227
11
{
228
11
    if (!valid_params(params)) {
229
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
230
0
        return SECFailure;
231
0
    }
232
233
11
    if (!(valid_privkey(params, privkey) && valid_ciphertext(params, ciphertext) && valid_secret(params, secret))) {
234
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
235
0
        return SECFailure;
236
0
    }
237
238
11
    if (params == params_kyber768_round3 || params == params_kyber768_round3_test_mode) {
239
0
        pqcrystals_kyber768_ref_dec(secret->data, ciphertext->data, privkey->data);
240
11
    } else if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) {
241
11
        libcrux_ml_kem_types_MlKemPrivateKey_55 private_key;
242
11
        memcpy(private_key.value, privkey->data, KYBER768_PRIVATE_KEY_BYTES);
243
244
11
        libcrux_ml_kem_mlkem768_MlKem768Ciphertext cipher_text;
245
11
        memcpy(cipher_text.value, ciphertext->data, KYBER768_CIPHERTEXT_BYTES);
246
247
11
        bool valid = libcrux_ml_kem_mlkem768_portable_validate_private_key(&private_key, &cipher_text);
248
11
        if (!valid) {
249
0
            PORT_SetError(SEC_ERROR_INVALID_ARGS);
250
0
            return SECFailure;
251
0
        }
252
253
11
        libcrux_ml_kem_mlkem768_portable_decapsulate(&private_key, &cipher_text, secret->data);
254
11
    } else {
255
        // unreachable
256
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
257
0
        return SECFailure;
258
0
    }
259
260
11
    return SECSuccess;
261
11
}