Coverage Report

Created: 2025-07-01 06:25

/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
0
{
40
0
    switch (params) {
41
0
        case params_kyber768_round3:
42
0
        case params_kyber768_round3_test_mode:
43
0
        case params_ml_kem768:
44
0
        case params_ml_kem768_test_mode:
45
0
            return true;
46
0
        default:
47
0
            return false;
48
0
    }
49
0
}
50
51
static bool
52
valid_pubkey(KyberParams params, const SECItem *pubkey)
53
0
{
54
0
    switch (params) {
55
0
        case params_kyber768_round3:
56
0
        case params_kyber768_round3_test_mode:
57
0
        case params_ml_kem768:
58
0
        case params_ml_kem768_test_mode:
59
0
            return pubkey && pubkey->len == KYBER768_PUBLIC_KEY_BYTES;
60
0
        default:
61
0
            return false;
62
0
    }
63
0
}
64
65
static bool
66
valid_privkey(KyberParams params, const SECItem *privkey)
67
0
{
68
0
    switch (params) {
69
0
        case params_kyber768_round3:
70
0
        case params_kyber768_round3_test_mode:
71
0
        case params_ml_kem768:
72
0
        case params_ml_kem768_test_mode:
73
0
            return privkey && privkey->len == KYBER768_PRIVATE_KEY_BYTES;
74
0
        default:
75
0
            return false;
76
0
    }
77
0
}
78
79
static bool
80
valid_ciphertext(KyberParams params, const SECItem *ciphertext)
81
0
{
82
0
    switch (params) {
83
0
        case params_kyber768_round3:
84
0
        case params_kyber768_round3_test_mode:
85
0
        case params_ml_kem768:
86
0
        case params_ml_kem768_test_mode:
87
0
            return ciphertext && ciphertext->len == KYBER768_CIPHERTEXT_BYTES;
88
0
        default:
89
0
            return false;
90
0
    }
91
0
}
92
93
static bool
94
valid_secret(KyberParams params, const SECItem *secret)
95
0
{
96
0
    switch (params) {
97
0
        case params_kyber768_round3:
98
0
        case params_kyber768_round3_test_mode:
99
0
        case params_ml_kem768:
100
0
        case params_ml_kem768_test_mode:
101
0
            return secret && secret->len == KYBER_SHARED_SECRET_BYTES;
102
0
        default:
103
0
            return false;
104
0
    }
105
0
}
106
107
static bool
108
valid_keypair_seed(KyberParams params, const SECItem *seed)
109
0
{
110
0
    switch (params) {
111
0
        case params_kyber768_round3:
112
0
        case params_kyber768_round3_test_mode:
113
0
        case params_ml_kem768:
114
0
        case params_ml_kem768_test_mode:
115
0
            return !seed || seed->len == KYBER_KEYPAIR_COIN_BYTES;
116
0
        default:
117
0
            return false;
118
0
    }
119
0
}
120
121
static bool
122
valid_enc_seed(KyberParams params, const SECItem *seed)
123
0
{
124
0
    switch (params) {
125
0
        case params_kyber768_round3:
126
0
        case params_ml_kem768:
127
0
            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
0
    }
134
0
}
135
136
SECStatus
137
Kyber_NewKey(KyberParams params, const SECItem *keypair_seed, SECItem *privkey, SECItem *pubkey)
138
0
{
139
0
    if (!valid_params(params)) {
140
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
141
0
        return SECFailure;
142
0
    }
143
144
0
    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
0
    uint8_t randbuf[KYBER_KEYPAIR_COIN_BYTES];
150
0
    uint8_t *coins;
151
0
    if (keypair_seed) {
152
0
        coins = keypair_seed->data;
153
0
    } else {
154
0
        if (RNG_GenerateGlobalRandomBytes(randbuf, sizeof randbuf) != SECSuccess) {
155
0
            PORT_SetError(SEC_ERROR_NEED_RANDOM);
156
0
            return SECFailure;
157
0
        }
158
0
        coins = randbuf;
159
0
    }
160
0
    NSS_CLASSIFY(coins, KYBER_KEYPAIR_COIN_BYTES);
161
0
    if (params == params_kyber768_round3 || params == params_kyber768_round3_test_mode) {
162
0
        pqcrystals_kyber768_ref_keypair_derand(pubkey->data, privkey->data, coins);
163
0
    } else if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) {
164
0
        libcrux_ml_kem_mlkem768_MlKem768KeyPair keys = libcrux_ml_kem_mlkem768_portable_generate_key_pair(coins);
165
0
        memcpy(pubkey->data, keys.pk.value, KYBER768_PUBLIC_KEY_BYTES);
166
0
        memcpy(privkey->data, keys.sk.value, KYBER768_PRIVATE_KEY_BYTES);
167
0
    } else {
168
        /* unreachable */
169
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
170
0
        return SECFailure;
171
0
    }
172
0
    NSS_DECLASSIFY(pubkey->data, pubkey->len);
173
0
    return SECSuccess;
174
0
}
175
176
SECStatus
177
Kyber_Encapsulate(KyberParams params, const SECItem *enc_seed, const SECItem *pubkey, SECItem *ciphertext, SECItem *secret)
178
0
{
179
0
    if (!valid_params(params)) {
180
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
181
0
        return SECFailure;
182
0
    }
183
184
0
    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
0
    uint8_t randbuf[KYBER_ENC_COIN_BYTES];
190
0
    uint8_t *coins;
191
0
    if (enc_seed) {
192
0
        coins = enc_seed->data;
193
0
    } else {
194
0
        if (RNG_GenerateGlobalRandomBytes(randbuf, sizeof randbuf) != SECSuccess) {
195
0
            PORT_SetError(SEC_ERROR_NEED_RANDOM);
196
0
            return SECFailure;
197
0
        }
198
0
        coins = randbuf;
199
0
    }
200
0
    NSS_CLASSIFY(coins, KYBER_ENC_COIN_BYTES);
201
0
    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
0
    } else if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) {
204
0
        libcrux_ml_kem_types_MlKemPublicKey_15 pk_value;
205
0
        memcpy(pk_value.value, pubkey->data, KYBER768_PUBLIC_KEY_BYTES);
206
207
0
        bool valid_pk = libcrux_ml_kem_mlkem768_portable_validate_public_key(&pk_value);
208
0
        if (!valid_pk) {
209
0
            PORT_SetError(SEC_ERROR_INVALID_ARGS);
210
0
            return SECFailure;
211
0
        }
212
213
0
        tuple_3c encap = libcrux_ml_kem_mlkem768_portable_encapsulate(&pk_value, coins);
214
0
        memcpy(ciphertext->data, encap.fst.value, KYBER768_CIPHERTEXT_BYTES);
215
0
        memcpy(secret->data, encap.snd, KYBER_SHARED_SECRET_BYTES);
216
0
    } else {
217
        /* unreachable */
218
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
219
0
        return SECFailure;
220
0
    }
221
222
0
    return SECSuccess;
223
0
}
224
225
SECStatus
226
Kyber_Decapsulate(KyberParams params, const SECItem *privkey, const SECItem *ciphertext, SECItem *secret)
227
0
{
228
0
    if (!valid_params(params)) {
229
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
230
0
        return SECFailure;
231
0
    }
232
233
0
    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
0
    if (params == params_kyber768_round3 || params == params_kyber768_round3_test_mode) {
239
0
        pqcrystals_kyber768_ref_dec(secret->data, ciphertext->data, privkey->data);
240
0
    } else if (params == params_ml_kem768 || params == params_ml_kem768_test_mode) {
241
0
        libcrux_ml_kem_types_MlKemPrivateKey_55 private_key;
242
0
        memcpy(private_key.value, privkey->data, KYBER768_PRIVATE_KEY_BYTES);
243
244
0
        libcrux_ml_kem_mlkem768_MlKem768Ciphertext cipher_text;
245
0
        memcpy(cipher_text.value, ciphertext->data, KYBER768_CIPHERTEXT_BYTES);
246
247
0
        bool valid = libcrux_ml_kem_mlkem768_portable_validate_private_key(&private_key, &cipher_text);
248
0
        if (!valid) {
249
0
            PORT_SetError(SEC_ERROR_INVALID_ARGS);
250
0
            return SECFailure;
251
0
        }
252
253
0
        libcrux_ml_kem_mlkem768_portable_decapsulate(&private_key, &cipher_text, secret->data);
254
0
    } else {
255
        // unreachable
256
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
257
0
        return SECFailure;
258
0
    }
259
260
0
    return SECSuccess;
261
0
}