/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 | } |