/src/boringssl/crypto/fipsmodule/mlkem/mlkem.cc.inc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2014 The BoringSSL Authors |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #include <openssl/base.h> |
16 | | |
17 | | #include <assert.h> |
18 | | #include <stdint.h> |
19 | | #include <stdlib.h> |
20 | | #include <string.h> |
21 | | |
22 | | #include <openssl/base.h> |
23 | | #include <openssl/bytestring.h> |
24 | | #include <openssl/mem.h> |
25 | | #include <openssl/rand.h> |
26 | | |
27 | | #include "../../internal.h" |
28 | | #include "../bcm_interface.h" |
29 | | #include "../delocate.h" |
30 | | #include "../keccak/internal.h" |
31 | | |
32 | | |
33 | | namespace mlkem { |
34 | | namespace { |
35 | | |
36 | | namespace fips { |
37 | | void ensure_keygen_self_test(); |
38 | | void ensure_encap_self_test(); |
39 | | void ensure_decap_self_test(); |
40 | | } // namespace fips |
41 | | |
42 | | // See |
43 | | // https://csrc.nist.gov/pubs/fips/203/final |
44 | | |
45 | 405k | static void prf(uint8_t *out, size_t out_len, const uint8_t in[33]) { |
46 | 405k | BORINGSSL_keccak(out, out_len, in, 33, boringssl_shake256); |
47 | 405k | } |
48 | | |
49 | | // Section 4.1 |
50 | 67.5k | void hash_h(uint8_t out[32], const uint8_t *in, size_t len) { |
51 | 67.5k | BORINGSSL_keccak(out, 32, in, len, boringssl_sha3_256); |
52 | 67.5k | } |
53 | | |
54 | 67.5k | void hash_g(uint8_t out[64], const uint8_t *in, size_t len) { |
55 | 67.5k | BORINGSSL_keccak(out, 64, in, len, boringssl_sha3_512); |
56 | 67.5k | } |
57 | | |
58 | | // This is called `J` in the spec. |
59 | | void kdf(uint8_t out[BCM_MLKEM_SHARED_SECRET_BYTES], |
60 | | const uint8_t failure_secret[32], const uint8_t *ciphertext, |
61 | 21 | size_t ciphertext_len) { |
62 | 21 | struct BORINGSSL_keccak_st st; |
63 | 21 | BORINGSSL_keccak_init(&st, boringssl_shake256); |
64 | 21 | BORINGSSL_keccak_absorb(&st, failure_secret, 32); |
65 | 21 | BORINGSSL_keccak_absorb(&st, ciphertext, ciphertext_len); |
66 | 21 | BORINGSSL_keccak_squeeze(&st, out, BCM_MLKEM_SHARED_SECRET_BYTES); |
67 | 21 | } |
68 | | |
69 | | // Constants that are common across all sizes. |
70 | 573M | #define DEGREE 256 |
71 | | const size_t kBarrettMultiplier = 5039; |
72 | | const unsigned kBarrettShift = 24; |
73 | | static const uint16_t kPrime = 3329; |
74 | | const int kLog2Prime = 12; |
75 | | const uint16_t kHalfPrime = (/*kPrime=*/3329 - 1) / 2; |
76 | | // kInverseDegree is 128^-1 mod 3329; 128 because kPrime does not have a 512th |
77 | | // root of unity. |
78 | | const uint16_t kInverseDegree = 3303; |
79 | | |
80 | | // Rank-specific constants. |
81 | 741 | #define RANK768 3 |
82 | | static const int kDU768 = 10; |
83 | | const int kDV768 = 4; |
84 | | #define RANK1024 4 |
85 | | static const int kDU1024 = 11; |
86 | | const int kDV1024 = 5; |
87 | | |
88 | 202k | constexpr size_t encoded_vector_size(int rank) { |
89 | 202k | return (kLog2Prime * DEGREE / 8) * static_cast<size_t>(rank); |
90 | 202k | } |
91 | | |
92 | 134k | constexpr size_t encoded_public_key_size(int rank) { |
93 | 134k | return encoded_vector_size(rank) + /*sizeof(rho)=*/32; |
94 | 134k | } |
95 | | |
96 | | static_assert(encoded_public_key_size(RANK768) == |
97 | | BCM_MLKEM768_PUBLIC_KEY_BYTES); |
98 | | static_assert(encoded_public_key_size(RANK1024) == |
99 | | BCM_MLKEM1024_PUBLIC_KEY_BYTES); |
100 | | |
101 | 247 | constexpr size_t compressed_vector_size(int rank) { |
102 | | // `if constexpr` isn't available in C++17. |
103 | 247 | return (rank == RANK768 ? kDU768 : kDU1024) * static_cast<size_t>(rank) * |
104 | 247 | DEGREE / 8; |
105 | 247 | } |
106 | | |
107 | 0 | constexpr size_t ciphertext_size(int rank) { |
108 | 0 | return compressed_vector_size(rank) + |
109 | 0 | (rank == RANK768 ? kDV768 : kDV1024) * DEGREE / 8; |
110 | 0 | } |
111 | | |
112 | | static_assert(ciphertext_size(RANK768) == BCM_MLKEM768_CIPHERTEXT_BYTES); |
113 | | static_assert(ciphertext_size(RANK1024) == BCM_MLKEM1024_CIPHERTEXT_BYTES); |
114 | | |
115 | | typedef struct scalar { |
116 | | // On every function entry and exit, 0 <= c < kPrime. |
117 | | uint16_t c[DEGREE]; |
118 | | } scalar; |
119 | | |
120 | | template <int RANK> |
121 | | struct vector { |
122 | | scalar v[RANK]; |
123 | | }; |
124 | | |
125 | | template <int RANK> |
126 | | struct matrix { |
127 | | scalar v[RANK][RANK]; |
128 | | }; |
129 | | |
130 | | // This bit of Python will be referenced in some of the following comments: |
131 | | // |
132 | | // p = 3329 |
133 | | // |
134 | | // def bitreverse(i): |
135 | | // ret = 0 |
136 | | // for n in range(7): |
137 | | // bit = i & 1 |
138 | | // ret <<= 1 |
139 | | // ret |= bit |
140 | | // i >>= 1 |
141 | | // return ret |
142 | | |
143 | | // kNTTRoots = [pow(17, bitreverse(i), p) for i in range(128)] |
144 | | const uint16_t kNTTRoots[128] = { |
145 | | 1, 1729, 2580, 3289, 2642, 630, 1897, 848, 1062, 1919, 193, 797, |
146 | | 2786, 3260, 569, 1746, 296, 2447, 1339, 1476, 3046, 56, 2240, 1333, |
147 | | 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, 289, 331, 3253, 1756, |
148 | | 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, |
149 | | 2319, 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, |
150 | | 2474, 3110, 1227, 910, 17, 2761, 583, 2649, 1637, 723, 2288, 1100, |
151 | | 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, 1703, 1651, 2789, 1789, |
152 | | 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, |
153 | | 1584, 2298, 2037, 3220, 375, 2549, 2090, 1645, 1063, 319, 2773, 757, |
154 | | 2099, 561, 2466, 2594, 2804, 1092, 403, 1026, 1143, 2150, 2775, 886, |
155 | | 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154, |
156 | | }; |
157 | | |
158 | | // kInverseNTTRoots = [pow(17, -bitreverse(i), p) for i in range(128)] |
159 | | const uint16_t kInverseNTTRoots[128] = { |
160 | | 1, 1600, 40, 749, 2481, 1432, 2699, 687, 1583, 2760, 69, 543, |
161 | | 2532, 3136, 1410, 2267, 2508, 1355, 450, 936, 447, 2794, 1235, 1903, |
162 | | 1996, 1089, 3273, 283, 1853, 1990, 882, 3033, 2419, 2102, 219, 855, |
163 | | 2681, 1848, 712, 682, 927, 1795, 461, 1891, 2877, 2522, 1894, 1010, |
164 | | 1414, 2009, 3296, 464, 2697, 816, 1352, 2679, 1274, 1052, 1025, 2132, |
165 | | 1573, 76, 2998, 3040, 1175, 2444, 394, 1219, 2300, 1455, 2117, 1607, |
166 | | 2443, 554, 1179, 2186, 2303, 2926, 2237, 525, 735, 863, 2768, 1230, |
167 | | 2572, 556, 3010, 2266, 1684, 1239, 780, 2954, 109, 1292, 1031, 1745, |
168 | | 2688, 3061, 992, 2596, 941, 892, 1021, 2390, 642, 1868, 2377, 1482, |
169 | | 1540, 540, 1678, 1626, 279, 314, 1173, 2573, 3096, 48, 667, 1920, |
170 | | 2229, 1041, 2606, 1692, 680, 2746, 568, 3312, |
171 | | }; |
172 | | |
173 | | // kModRoots = [pow(17, 2*bitreverse(i) + 1, p) for i in range(128)] |
174 | | const uint16_t kModRoots[128] = { |
175 | | 17, 3312, 2761, 568, 583, 2746, 2649, 680, 1637, 1692, 723, 2606, |
176 | | 2288, 1041, 1100, 2229, 1409, 1920, 2662, 667, 3281, 48, 233, 3096, |
177 | | 756, 2573, 2156, 1173, 3015, 314, 3050, 279, 1703, 1626, 1651, 1678, |
178 | | 2789, 540, 1789, 1540, 1847, 1482, 952, 2377, 1461, 1868, 2687, 642, |
179 | | 939, 2390, 2308, 1021, 2437, 892, 2388, 941, 733, 2596, 2337, 992, |
180 | | 268, 3061, 641, 2688, 1584, 1745, 2298, 1031, 2037, 1292, 3220, 109, |
181 | | 375, 2954, 2549, 780, 2090, 1239, 1645, 1684, 1063, 2266, 319, 3010, |
182 | | 2773, 556, 757, 2572, 2099, 1230, 561, 2768, 2466, 863, 2594, 735, |
183 | | 2804, 525, 1092, 2237, 403, 2926, 1026, 2303, 1143, 2186, 2150, 1179, |
184 | | 2775, 554, 886, 2443, 1722, 1607, 1212, 2117, 1874, 1455, 1029, 2300, |
185 | | 2110, 1219, 2935, 394, 885, 2444, 2154, 1175, |
186 | | }; |
187 | | |
188 | | // reduce_once reduces 0 <= x < 2*kPrime, mod kPrime. |
189 | 1.53G | uint16_t reduce_once(uint16_t x) { |
190 | 1.53G | declassify_assert(x < 2 * kPrime); |
191 | 1.53G | const uint16_t subtracted = x - kPrime; |
192 | 1.53G | uint16_t mask = 0u - (subtracted >> 15); |
193 | | // Although this is a constant-time select, we omit a value barrier here. |
194 | | // Value barriers impede auto-vectorization (likely because it forces the |
195 | | // value to transit through a general-purpose register). On AArch64, this is a |
196 | | // difference of 2x. |
197 | | // |
198 | | // We usually add value barriers to selects because Clang turns consecutive |
199 | | // selects with the same condition into a branch instead of CMOV/CSEL. This |
200 | | // condition does not occur in ML-KEM, so omitting it seems to be safe so far, |
201 | | // but see |scalar_centered_binomial_distribution_eta_2_with_prf|. |
202 | 1.53G | return (mask & x) | (~mask & subtracted); |
203 | 1.53G | } |
204 | | |
205 | | // constant time reduce x mod kPrime using Barrett reduction. x must be less |
206 | | // than kPrime + 2×kPrime². |
207 | 597M | static uint16_t reduce(uint32_t x) { |
208 | 597M | declassify_assert(x < kPrime + 2u * kPrime * kPrime); |
209 | 597M | uint64_t product = (uint64_t)x * kBarrettMultiplier; |
210 | 597M | uint32_t quotient = (uint32_t)(product >> kBarrettShift); |
211 | 597M | uint32_t remainder = x - quotient * kPrime; |
212 | 597M | return reduce_once(remainder); |
213 | 597M | } |
214 | | |
215 | 247 | void scalar_zero(scalar *out) { OPENSSL_memset(out, 0, sizeof(*out)); } |
216 | | |
217 | | template <int RANK> |
218 | 67.5k | void vector_zero(vector<RANK> *out) { |
219 | 67.5k | OPENSSL_memset(out->v, 0, sizeof(scalar) * RANK); |
220 | 67.5k | } bcm.cc:void mlkem::(anonymous namespace)::vector_zero<3>(mlkem::(anonymous namespace)::vector<3>*) Line | Count | Source | 218 | 67.5k | void vector_zero(vector<RANK> *out) { | 219 | 67.5k | OPENSSL_memset(out->v, 0, sizeof(scalar) * RANK); | 220 | 67.5k | } |
bcm.cc:void mlkem::(anonymous namespace)::vector_zero<4>(mlkem::(anonymous namespace)::vector<4>*) Line | Count | Source | 218 | 8 | void vector_zero(vector<RANK> *out) { | 219 | 8 | OPENSSL_memset(out->v, 0, sizeof(scalar) * RANK); | 220 | 8 | } |
|
221 | | |
222 | | // In place number theoretic transform of a given scalar. |
223 | | // Note that MLKEM's kPrime 3329 does not have a 512th root of unity, so this |
224 | | // transform leaves off the last iteration of the usual FFT code, with the 128 |
225 | | // relevant roots of unity being stored in |kNTTRoots|. This means the output |
226 | | // should be seen as 128 elements in GF(3329^2), with the coefficients of the |
227 | | // elements being consecutive entries in |s->c|. |
228 | 404k | static void scalar_ntt(scalar *s) { |
229 | 404k | int offset = DEGREE; |
230 | | // `int` is used here because using `size_t` throughout caused a ~5% slowdown |
231 | | // with Clang 14 on Aarch64. |
232 | 3.23M | for (int step = 1; step < DEGREE / 2; step <<= 1) { |
233 | 2.83M | offset >>= 1; |
234 | 2.83M | int k = 0; |
235 | 54.2M | for (int i = 0; i < step; i++) { |
236 | 51.3M | const uint32_t step_root = kNTTRoots[i + step]; |
237 | 413M | for (int j = k; j < k + offset; j++) { |
238 | 362M | uint16_t odd = reduce(step_root * s->c[j + offset]); |
239 | 362M | uint16_t even = s->c[j]; |
240 | 362M | s->c[j] = reduce_once(odd + even); |
241 | 362M | s->c[j + offset] = reduce_once(even - odd + kPrime); |
242 | 362M | } |
243 | 51.3M | k += 2 * offset; |
244 | 51.3M | } |
245 | 2.83M | } |
246 | 404k | } |
247 | | |
248 | | template <int RANK> |
249 | 134k | static void vector_ntt(vector<RANK> *a) { |
250 | 539k | for (int i = 0; i < RANK; i++) { |
251 | 404k | scalar_ntt(&a->v[i]); |
252 | 404k | } |
253 | 134k | } bcm.cc:void mlkem::(anonymous namespace)::vector_ntt<3>(mlkem::(anonymous namespace)::vector<3>*) Line | Count | Source | 249 | 134k | static void vector_ntt(vector<RANK> *a) { | 250 | 539k | for (int i = 0; i < RANK; i++) { | 251 | 404k | scalar_ntt(&a->v[i]); | 252 | 404k | } | 253 | 134k | } |
bcm.cc:void mlkem::(anonymous namespace)::vector_ntt<4>(mlkem::(anonymous namespace)::vector<4>*) Line | Count | Source | 249 | 16 | static void vector_ntt(vector<RANK> *a) { | 250 | 80 | for (int i = 0; i < RANK; i++) { | 251 | 64 | scalar_ntt(&a->v[i]); | 252 | 64 | } | 253 | 16 | } |
|
254 | | |
255 | | // In place inverse number theoretic transform of a given scalar, with pairs of |
256 | | // entries of s->v being interpreted as elements of GF(3329^2). Just as with the |
257 | | // number theoretic transform, this leaves off the first step of the normal iFFT |
258 | | // to account for the fact that 3329 does not have a 512th root of unity, using |
259 | | // the precomputed 128 roots of unity stored in |kInverseNTTRoots|. |
260 | 925 | void scalar_inverse_ntt(scalar *s) { |
261 | 925 | int step = DEGREE / 2; |
262 | | // `int` is used here because using `size_t` throughout caused a ~5% slowdown |
263 | | // with Clang 14 on Aarch64. |
264 | 7.40k | for (int offset = 2; offset < DEGREE; offset <<= 1) { |
265 | 6.47k | step >>= 1; |
266 | 6.47k | int k = 0; |
267 | 123k | for (int i = 0; i < step; i++) { |
268 | 117k | uint32_t step_root = kInverseNTTRoots[i + step]; |
269 | 946k | for (int j = k; j < k + offset; j++) { |
270 | 828k | uint16_t odd = s->c[j + offset]; |
271 | 828k | uint16_t even = s->c[j]; |
272 | 828k | s->c[j] = reduce_once(odd + even); |
273 | 828k | s->c[j + offset] = reduce(step_root * (even - odd + kPrime)); |
274 | 828k | } |
275 | 117k | k += 2 * offset; |
276 | 117k | } |
277 | 6.47k | } |
278 | 237k | for (int i = 0; i < DEGREE; i++) { |
279 | 236k | s->c[i] = reduce(s->c[i] * kInverseDegree); |
280 | 236k | } |
281 | 925 | } |
282 | | |
283 | | template <int RANK> |
284 | 226 | void vector_inverse_ntt(vector<RANK> *a) { |
285 | 904 | for (int i = 0; i < RANK; i++) { |
286 | 678 | scalar_inverse_ntt(&a->v[i]); |
287 | 678 | } |
288 | 226 | } bcm.cc:void mlkem::(anonymous namespace)::vector_inverse_ntt<3>(mlkem::(anonymous namespace)::vector<3>*) Line | Count | Source | 284 | 226 | void vector_inverse_ntt(vector<RANK> *a) { | 285 | 904 | for (int i = 0; i < RANK; i++) { | 286 | 678 | scalar_inverse_ntt(&a->v[i]); | 287 | 678 | } | 288 | 226 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::vector_inverse_ntt<4>(mlkem::(anonymous namespace)::vector<4>*) |
289 | | |
290 | 811k | void scalar_add(scalar *lhs, const scalar *rhs) { |
291 | 208M | for (int i = 0; i < DEGREE; i++) { |
292 | 207M | lhs->c[i] = reduce_once(lhs->c[i] + rhs->c[i]); |
293 | 207M | } |
294 | 811k | } |
295 | | |
296 | 21 | void scalar_sub(scalar *lhs, const scalar *rhs) { |
297 | 5.39k | for (int i = 0; i < DEGREE; i++) { |
298 | 5.37k | lhs->c[i] = reduce_once(lhs->c[i] - rhs->c[i] + kPrime); |
299 | 5.37k | } |
300 | 21 | } |
301 | | |
302 | | // Multiplying two scalars in the number theoretically transformed state. Since |
303 | | // 3329 does not have a 512th root of unity, this means we have to interpret |
304 | | // the 2*ith and (2*i+1)th entries of the scalar as elements of GF(3329)[X]/(X^2 |
305 | | // - 17^(2*bitreverse(i)+1)) The value of 17^(2*bitreverse(i)+1) mod 3329 is |
306 | | // stored in the precomputed |kModRoots| table. Note that our Barrett transform |
307 | | // only allows us to multipy two reduced numbers together, so we need some |
308 | | // intermediate reduction steps, even if an uint64_t could hold 3 multiplied |
309 | | // numbers. |
310 | 608k | void scalar_mult(scalar *out, const scalar *lhs, const scalar *rhs) { |
311 | 78.4M | for (int i = 0; i < DEGREE / 2; i++) { |
312 | 77.8M | uint32_t real_real = (uint32_t)lhs->c[2 * i] * rhs->c[2 * i]; |
313 | 77.8M | uint32_t img_img = (uint32_t)lhs->c[2 * i + 1] * rhs->c[2 * i + 1]; |
314 | 77.8M | uint32_t real_img = (uint32_t)lhs->c[2 * i] * rhs->c[2 * i + 1]; |
315 | 77.8M | uint32_t img_real = (uint32_t)lhs->c[2 * i + 1] * rhs->c[2 * i]; |
316 | 77.8M | out->c[2 * i] = |
317 | 77.8M | reduce(real_real + (uint32_t)reduce(img_img) * kModRoots[i]); |
318 | 77.8M | out->c[2 * i + 1] = reduce(img_real + real_img); |
319 | 77.8M | } |
320 | 608k | } |
321 | | |
322 | | template <int RANK> |
323 | 67.5k | void vector_add(vector<RANK> *lhs, const vector<RANK> *rhs) { |
324 | 270k | for (int i = 0; i < RANK; i++) { |
325 | 202k | scalar_add(&lhs->v[i], &rhs->v[i]); |
326 | 202k | } |
327 | 67.5k | } bcm.cc:void mlkem::(anonymous namespace)::vector_add<3>(mlkem::(anonymous namespace)::vector<3>*, mlkem::(anonymous namespace)::vector<3> const*) Line | Count | Source | 323 | 67.5k | void vector_add(vector<RANK> *lhs, const vector<RANK> *rhs) { | 324 | 270k | for (int i = 0; i < RANK; i++) { | 325 | 202k | scalar_add(&lhs->v[i], &rhs->v[i]); | 326 | 202k | } | 327 | 67.5k | } |
bcm.cc:void mlkem::(anonymous namespace)::vector_add<4>(mlkem::(anonymous namespace)::vector<4>*, mlkem::(anonymous namespace)::vector<4> const*) Line | Count | Source | 323 | 8 | void vector_add(vector<RANK> *lhs, const vector<RANK> *rhs) { | 324 | 40 | for (int i = 0; i < RANK; i++) { | 325 | 32 | scalar_add(&lhs->v[i], &rhs->v[i]); | 326 | 32 | } | 327 | 8 | } |
|
328 | | |
329 | | template <int RANK> |
330 | | static void matrix_mult(vector<RANK> *out, const matrix<RANK> *m, |
331 | 226 | const vector<RANK> *a) { |
332 | 226 | vector_zero(out); |
333 | 904 | for (int i = 0; i < RANK; i++) { |
334 | 2.71k | for (int j = 0; j < RANK; j++) { |
335 | 2.03k | scalar product; |
336 | 2.03k | scalar_mult(&product, &m->v[i][j], &a->v[j]); |
337 | 2.03k | scalar_add(&out->v[i], &product); |
338 | 2.03k | } |
339 | 678 | } |
340 | 226 | } bcm.cc:void mlkem::(anonymous namespace)::matrix_mult<3>(mlkem::(anonymous namespace)::vector<3>*, mlkem::(anonymous namespace)::matrix<3> const*, mlkem::(anonymous namespace)::vector<3> const*) Line | Count | Source | 331 | 226 | const vector<RANK> *a) { | 332 | 226 | vector_zero(out); | 333 | 904 | for (int i = 0; i < RANK; i++) { | 334 | 2.71k | for (int j = 0; j < RANK; j++) { | 335 | 2.03k | scalar product; | 336 | 2.03k | scalar_mult(&product, &m->v[i][j], &a->v[j]); | 337 | 2.03k | scalar_add(&out->v[i], &product); | 338 | 2.03k | } | 339 | 678 | } | 340 | 226 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::matrix_mult<4>(mlkem::(anonymous namespace)::vector<4>*, mlkem::(anonymous namespace)::matrix<4> const*, mlkem::(anonymous namespace)::vector<4> const*) |
341 | | |
342 | | template <int RANK> |
343 | | void matrix_mult_transpose(vector<RANK> *out, const matrix<RANK> *m, |
344 | 67.2k | const vector<RANK> *a) { |
345 | 67.2k | vector_zero(out); |
346 | 269k | for (int i = 0; i < RANK; i++) { |
347 | 807k | for (int j = 0; j < RANK; j++) { |
348 | 605k | scalar product; |
349 | 605k | scalar_mult(&product, &m->v[j][i], &a->v[j]); |
350 | 605k | scalar_add(&out->v[i], &product); |
351 | 605k | } |
352 | 201k | } |
353 | 67.2k | } bcm.cc:void mlkem::(anonymous namespace)::matrix_mult_transpose<3>(mlkem::(anonymous namespace)::vector<3>*, mlkem::(anonymous namespace)::matrix<3> const*, mlkem::(anonymous namespace)::vector<3> const*) Line | Count | Source | 344 | 67.2k | const vector<RANK> *a) { | 345 | 67.2k | vector_zero(out); | 346 | 269k | for (int i = 0; i < RANK; i++) { | 347 | 807k | for (int j = 0; j < RANK; j++) { | 348 | 605k | scalar product; | 349 | 605k | scalar_mult(&product, &m->v[j][i], &a->v[j]); | 350 | 605k | scalar_add(&out->v[i], &product); | 351 | 605k | } | 352 | 201k | } | 353 | 67.2k | } |
bcm.cc:void mlkem::(anonymous namespace)::matrix_mult_transpose<4>(mlkem::(anonymous namespace)::vector<4>*, mlkem::(anonymous namespace)::matrix<4> const*, mlkem::(anonymous namespace)::vector<4> const*) Line | Count | Source | 344 | 8 | const vector<RANK> *a) { | 345 | 8 | vector_zero(out); | 346 | 40 | for (int i = 0; i < RANK; i++) { | 347 | 160 | for (int j = 0; j < RANK; j++) { | 348 | 128 | scalar product; | 349 | 128 | scalar_mult(&product, &m->v[j][i], &a->v[j]); | 350 | 128 | scalar_add(&out->v[i], &product); | 351 | 128 | } | 352 | 32 | } | 353 | 8 | } |
|
354 | | |
355 | | template <int RANK> |
356 | | void scalar_inner_product(scalar *out, const vector<RANK> *lhs, |
357 | 247 | const vector<RANK> *rhs) { |
358 | 247 | scalar_zero(out); |
359 | 988 | for (int i = 0; i < RANK; i++) { |
360 | 741 | scalar product; |
361 | 741 | scalar_mult(&product, &lhs->v[i], &rhs->v[i]); |
362 | 741 | scalar_add(out, &product); |
363 | 741 | } |
364 | 247 | } bcm.cc:void mlkem::(anonymous namespace)::scalar_inner_product<3>(mlkem::(anonymous namespace)::scalar*, mlkem::(anonymous namespace)::vector<3> const*, mlkem::(anonymous namespace)::vector<3> const*) Line | Count | Source | 357 | 247 | const vector<RANK> *rhs) { | 358 | 247 | scalar_zero(out); | 359 | 988 | for (int i = 0; i < RANK; i++) { | 360 | 741 | scalar product; | 361 | 741 | scalar_mult(&product, &lhs->v[i], &rhs->v[i]); | 362 | 741 | scalar_add(out, &product); | 363 | 741 | } | 364 | 247 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::scalar_inner_product<4>(mlkem::(anonymous namespace)::scalar*, mlkem::(anonymous namespace)::vector<4> const*, mlkem::(anonymous namespace)::vector<4> const*) |
365 | | |
366 | | // Algorithm 6 from the spec. Rejection samples a Keccak stream to get |
367 | | // uniformly distributed elements. This is used for matrix expansion and only |
368 | | // operates on public inputs. |
369 | | static void scalar_from_keccak_vartime(scalar *out, |
370 | 607k | struct BORINGSSL_keccak_st *keccak_ctx) { |
371 | 607k | assert(keccak_ctx->squeeze_offset == 0); |
372 | 607k | assert(keccak_ctx->rate_bytes == 168); |
373 | 607k | static_assert(168 % 3 == 0, "block and coefficient boundaries do not align"); |
374 | | |
375 | 607k | int done = 0; |
376 | 2.43M | while (done < DEGREE) { |
377 | 1.82M | uint8_t block[168]; |
378 | 1.82M | BORINGSSL_keccak_squeeze(keccak_ctx, block, sizeof(block)); |
379 | 97.6M | for (size_t i = 0; i < sizeof(block) && done < DEGREE; i += 3) { |
380 | 95.8M | uint16_t d1 = block[i] + 256 * (block[i + 1] % 16); |
381 | 95.8M | uint16_t d2 = block[i + 1] / 16 + 16 * block[i + 2]; |
382 | 95.8M | if (d1 < kPrime) { |
383 | 77.9M | out->c[done++] = d1; |
384 | 77.9M | } |
385 | 95.8M | if (d2 < kPrime && done < DEGREE) { |
386 | 77.6M | out->c[done++] = d2; |
387 | 77.6M | } |
388 | 95.8M | } |
389 | 1.82M | } |
390 | 607k | } |
391 | | |
392 | | // Algorithm 7 from the spec, with eta fixed to two and the PRF call |
393 | | // included. Creates binominally distributed elements by sampling 2*|eta| bits, |
394 | | // and setting the coefficient to the count of the first bits minus the count of |
395 | | // the second bits, resulting in a centered binomial distribution. Since eta is |
396 | | // two this gives -2/2 with a probability of 1/16, -1/1 with probability 1/4, |
397 | | // and 0 with probability 3/8. |
398 | | void scalar_centered_binomial_distribution_eta_2_with_prf( |
399 | 405k | scalar *out, const uint8_t input[33]) { |
400 | 405k | uint8_t entropy[128]; |
401 | 405k | static_assert(sizeof(entropy) == 2 * /*kEta=*/2 * DEGREE / 8); |
402 | 405k | prf(entropy, sizeof(entropy), input); |
403 | | |
404 | 52.2M | for (int i = 0; i < DEGREE; i += 2) { |
405 | 51.8M | uint8_t byte = entropy[i / 2]; |
406 | | |
407 | 51.8M | uint16_t value = (byte & 1) + ((byte >> 1) & 1); |
408 | 51.8M | value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); |
409 | | // Add |kPrime| if |value| underflowed. See |reduce_once| for a discussion |
410 | | // on why the value barrier is omitted. While this could have been written |
411 | | // reduce_once(value + kPrime), this is one extra addition and small range |
412 | | // of |value| tempts some versions of Clang to emit a branch. |
413 | 51.8M | uint16_t mask = 0u - (value >> 15); |
414 | 51.8M | out->c[i] = ((value + kPrime) & mask) | (value & ~mask); |
415 | | |
416 | 51.8M | byte >>= 4; |
417 | 51.8M | value = (byte & 1) + ((byte >> 1) & 1); |
418 | 51.8M | value -= ((byte >> 2) & 1) + ((byte >> 3) & 1); |
419 | | // See above. |
420 | 51.8M | mask = 0u - (value >> 15); |
421 | 51.8M | out->c[i + 1] = ((value + kPrime) & mask) | (value & ~mask); |
422 | 51.8M | } |
423 | 405k | } |
424 | | |
425 | | // Generates a secret vector by using |
426 | | // |scalar_centered_binomial_distribution_eta_2_with_prf|, using the given seed |
427 | | // appending and incrementing |counter| for entry of the vector. |
428 | | template <int RANK> |
429 | | void vector_generate_secret_eta_2(vector<RANK> *out, uint8_t *counter, |
430 | 135k | const uint8_t seed[32]) { |
431 | 135k | uint8_t input[33]; |
432 | 135k | OPENSSL_memcpy(input, seed, 32); |
433 | 540k | for (int i = 0; i < RANK; i++) { |
434 | 405k | input[32] = (*counter)++; |
435 | 405k | scalar_centered_binomial_distribution_eta_2_with_prf(&out->v[i], input); |
436 | 405k | } |
437 | 135k | } bcm.cc:void mlkem::(anonymous namespace)::vector_generate_secret_eta_2<3>(mlkem::(anonymous namespace)::vector<3>*, unsigned char*, unsigned char const*) Line | Count | Source | 430 | 135k | const uint8_t seed[32]) { | 431 | 135k | uint8_t input[33]; | 432 | 135k | OPENSSL_memcpy(input, seed, 32); | 433 | 540k | for (int i = 0; i < RANK; i++) { | 434 | 405k | input[32] = (*counter)++; | 435 | 405k | scalar_centered_binomial_distribution_eta_2_with_prf(&out->v[i], input); | 436 | 405k | } | 437 | 135k | } |
bcm.cc:void mlkem::(anonymous namespace)::vector_generate_secret_eta_2<4>(mlkem::(anonymous namespace)::vector<4>*, unsigned char*, unsigned char const*) Line | Count | Source | 430 | 16 | const uint8_t seed[32]) { | 431 | 16 | uint8_t input[33]; | 432 | 16 | OPENSSL_memcpy(input, seed, 32); | 433 | 80 | for (int i = 0; i < RANK; i++) { | 434 | 64 | input[32] = (*counter)++; | 435 | 64 | scalar_centered_binomial_distribution_eta_2_with_prf(&out->v[i], input); | 436 | 64 | } | 437 | 16 | } |
|
438 | | |
439 | | // Expands the matrix of a seed for key generation and for encaps-CPA. |
440 | | template <int RANK> |
441 | 67.5k | void matrix_expand(matrix<RANK> *out, const uint8_t rho[32]) { |
442 | 67.5k | uint8_t input[34]; |
443 | 67.5k | OPENSSL_memcpy(input, rho, 32); |
444 | 270k | for (int i = 0; i < RANK; i++) { |
445 | 810k | for (int j = 0; j < RANK; j++) { |
446 | 607k | input[32] = i; |
447 | 607k | input[33] = j; |
448 | 607k | struct BORINGSSL_keccak_st keccak_ctx; |
449 | 607k | BORINGSSL_keccak_init(&keccak_ctx, boringssl_shake128); |
450 | 607k | BORINGSSL_keccak_absorb(&keccak_ctx, input, sizeof(input)); |
451 | 607k | scalar_from_keccak_vartime(&out->v[i][j], &keccak_ctx); |
452 | 607k | } |
453 | 202k | } |
454 | 67.5k | } bcm.cc:void mlkem::(anonymous namespace)::matrix_expand<3>(mlkem::(anonymous namespace)::matrix<3>*, unsigned char const*) Line | Count | Source | 441 | 67.5k | void matrix_expand(matrix<RANK> *out, const uint8_t rho[32]) { | 442 | 67.5k | uint8_t input[34]; | 443 | 67.5k | OPENSSL_memcpy(input, rho, 32); | 444 | 270k | for (int i = 0; i < RANK; i++) { | 445 | 810k | for (int j = 0; j < RANK; j++) { | 446 | 607k | input[32] = i; | 447 | 607k | input[33] = j; | 448 | 607k | struct BORINGSSL_keccak_st keccak_ctx; | 449 | 607k | BORINGSSL_keccak_init(&keccak_ctx, boringssl_shake128); | 450 | 607k | BORINGSSL_keccak_absorb(&keccak_ctx, input, sizeof(input)); | 451 | 607k | scalar_from_keccak_vartime(&out->v[i][j], &keccak_ctx); | 452 | 607k | } | 453 | 202k | } | 454 | 67.5k | } |
bcm.cc:void mlkem::(anonymous namespace)::matrix_expand<4>(mlkem::(anonymous namespace)::matrix<4>*, unsigned char const*) Line | Count | Source | 441 | 8 | void matrix_expand(matrix<RANK> *out, const uint8_t rho[32]) { | 442 | 8 | uint8_t input[34]; | 443 | 8 | OPENSSL_memcpy(input, rho, 32); | 444 | 40 | for (int i = 0; i < RANK; i++) { | 445 | 160 | for (int j = 0; j < RANK; j++) { | 446 | 128 | input[32] = i; | 447 | 128 | input[33] = j; | 448 | 128 | struct BORINGSSL_keccak_st keccak_ctx; | 449 | 128 | BORINGSSL_keccak_init(&keccak_ctx, boringssl_shake128); | 450 | 128 | BORINGSSL_keccak_absorb(&keccak_ctx, input, sizeof(input)); | 451 | 128 | scalar_from_keccak_vartime(&out->v[i][j], &keccak_ctx); | 452 | 128 | } | 453 | 32 | } | 454 | 8 | } |
|
455 | | |
456 | | const uint8_t kMasks[8] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; |
457 | | |
458 | 202k | void scalar_encode(uint8_t *out, const scalar *s, int bits) { |
459 | 202k | assert(bits <= (int)sizeof(*s->c) * 8 && bits != 1); |
460 | | |
461 | 202k | uint8_t out_byte = 0; |
462 | 202k | int out_byte_bits = 0; |
463 | | |
464 | 52.1M | for (int i = 0; i < DEGREE; i++) { |
465 | 51.9M | uint16_t element = s->c[i]; |
466 | 51.9M | int element_bits_done = 0; |
467 | | |
468 | 155M | while (element_bits_done < bits) { |
469 | 103M | int chunk_bits = bits - element_bits_done; |
470 | 103M | int out_bits_remaining = 8 - out_byte_bits; |
471 | 103M | if (chunk_bits >= out_bits_remaining) { |
472 | 77.7M | chunk_bits = out_bits_remaining; |
473 | 77.7M | out_byte |= (element & kMasks[chunk_bits - 1]) << out_byte_bits; |
474 | 77.7M | *out = out_byte; |
475 | 77.7M | out++; |
476 | 77.7M | out_byte_bits = 0; |
477 | 77.7M | out_byte = 0; |
478 | 77.7M | } else { |
479 | 25.9M | out_byte |= (element & kMasks[chunk_bits - 1]) << out_byte_bits; |
480 | 25.9M | out_byte_bits += chunk_bits; |
481 | 25.9M | } |
482 | | |
483 | 103M | element_bits_done += chunk_bits; |
484 | 103M | element >>= chunk_bits; |
485 | 103M | } |
486 | 51.9M | } |
487 | | |
488 | 202k | if (out_byte_bits > 0) { |
489 | 0 | *out = out_byte; |
490 | 0 | } |
491 | 202k | } |
492 | | |
493 | | // scalar_encode_1 is |scalar_encode| specialised for |bits| == 1. |
494 | 21 | void scalar_encode_1(uint8_t out[32], const scalar *s) { |
495 | 693 | for (int i = 0; i < DEGREE; i += 8) { |
496 | 672 | uint8_t out_byte = 0; |
497 | 6.04k | for (int j = 0; j < 8; j++) { |
498 | 5.37k | out_byte |= (s->c[i + j] & 1) << j; |
499 | 5.37k | } |
500 | 672 | *out = out_byte; |
501 | 672 | out++; |
502 | 672 | } |
503 | 21 | } |
504 | | |
505 | | // Encodes an entire vector into 32*|RANK|*|bits| bytes. Note that since 256 |
506 | | // (DEGREE) is divisible by 8, the individual vector entries will always fill a |
507 | | // whole number of bytes, so we do not need to worry about bit packing here. |
508 | | template <int RANK> |
509 | 67.5k | void vector_encode(uint8_t *out, const vector<RANK> *a, int bits) { |
510 | 270k | for (int i = 0; i < RANK; i++) { |
511 | 202k | scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); |
512 | 202k | } |
513 | 67.5k | } bcm.cc:void mlkem::(anonymous namespace)::vector_encode<3>(unsigned char*, mlkem::(anonymous namespace)::vector<3> const*, int) Line | Count | Source | 509 | 67.5k | void vector_encode(uint8_t *out, const vector<RANK> *a, int bits) { | 510 | 270k | for (int i = 0; i < RANK; i++) { | 511 | 202k | scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); | 512 | 202k | } | 513 | 67.5k | } |
bcm.cc:void mlkem::(anonymous namespace)::vector_encode<4>(unsigned char*, mlkem::(anonymous namespace)::vector<4> const*, int) Line | Count | Source | 509 | 8 | void vector_encode(uint8_t *out, const vector<RANK> *a, int bits) { | 510 | 40 | for (int i = 0; i < RANK; i++) { | 511 | 32 | scalar_encode(out + i * bits * DEGREE / 8, &a->v[i], bits); | 512 | 32 | } | 513 | 8 | } |
|
514 | | |
515 | | // scalar_decode parses |DEGREE * bits| bits from |in| into |DEGREE| values in |
516 | | // |out|. It returns one on success and zero if any parsed value is >= |
517 | | // |kPrime|. |
518 | 881 | int scalar_decode(scalar *out, const uint8_t *in, int bits) { |
519 | 881 | assert(bits <= (int)sizeof(*out->c) * 8 && bits != 1); |
520 | | |
521 | 881 | uint8_t in_byte = 0; |
522 | 881 | int in_byte_bits_left = 0; |
523 | | |
524 | 217k | for (int i = 0; i < DEGREE; i++) { |
525 | 216k | uint16_t element = 0; |
526 | 216k | int element_bits_done = 0; |
527 | | |
528 | 643k | while (element_bits_done < bits) { |
529 | 427k | if (in_byte_bits_left == 0) { |
530 | 314k | in_byte = *in; |
531 | 314k | in++; |
532 | 314k | in_byte_bits_left = 8; |
533 | 314k | } |
534 | | |
535 | 427k | int chunk_bits = bits - element_bits_done; |
536 | 427k | if (chunk_bits > in_byte_bits_left) { |
537 | 210k | chunk_bits = in_byte_bits_left; |
538 | 210k | } |
539 | | |
540 | 427k | element |= (in_byte & kMasks[chunk_bits - 1]) << element_bits_done; |
541 | 427k | in_byte_bits_left -= chunk_bits; |
542 | 427k | in_byte >>= chunk_bits; |
543 | | |
544 | 427k | element_bits_done += chunk_bits; |
545 | 427k | } |
546 | | |
547 | | // An element is only out of range in the case of invalid input, in which |
548 | | // case it is okay to leak the comparison. |
549 | 216k | if (constant_time_declassify_int(element >= kPrime)) { |
550 | 44 | return 0; |
551 | 44 | } |
552 | 216k | out->c[i] = element; |
553 | 216k | } |
554 | | |
555 | 837 | return 1; |
556 | 881 | } |
557 | | |
558 | | // scalar_decode_1 is |scalar_decode| specialised for |bits| == 1. |
559 | 226 | void scalar_decode_1(scalar *out, const uint8_t in[32]) { |
560 | 7.45k | for (int i = 0; i < DEGREE; i += 8) { |
561 | 7.23k | uint8_t in_byte = *in; |
562 | 7.23k | in++; |
563 | 65.0k | for (int j = 0; j < 8; j++) { |
564 | 57.8k | out->c[i + j] = in_byte & 1; |
565 | 57.8k | in_byte >>= 1; |
566 | 57.8k | } |
567 | 7.23k | } |
568 | 226 | } |
569 | | |
570 | | // Decodes 32*|RANK|*|bits| bytes from |in| into |out|. It returns one on |
571 | | // success or zero if any parsed value is >= |kPrime|. |
572 | | template <int RANK> |
573 | 302 | static int vector_decode(vector<RANK> *out, const uint8_t *in, int bits) { |
574 | 1.11k | for (int i = 0; i < RANK; i++) { |
575 | 860 | if (!scalar_decode(&out->v[i], in + i * bits * DEGREE / 8, bits)) { |
576 | 44 | return 0; |
577 | 44 | } |
578 | 860 | } |
579 | 258 | return 1; |
580 | 302 | } bcm.cc:int mlkem::(anonymous namespace)::vector_decode<3>(mlkem::(anonymous namespace)::vector<3>*, unsigned char const*, int) Line | Count | Source | 573 | 302 | static int vector_decode(vector<RANK> *out, const uint8_t *in, int bits) { | 574 | 1.11k | for (int i = 0; i < RANK; i++) { | 575 | 860 | if (!scalar_decode(&out->v[i], in + i * bits * DEGREE / 8, bits)) { | 576 | 44 | return 0; | 577 | 44 | } | 578 | 860 | } | 579 | 258 | return 1; | 580 | 302 | } |
Unexecuted instantiation: bcm.cc:int mlkem::(anonymous namespace)::vector_decode<4>(mlkem::(anonymous namespace)::vector<4>*, unsigned char const*, int) |
581 | | |
582 | | // Compresses (lossily) an input |x| mod 3329 into |bits| many bits by grouping |
583 | | // numbers close to each other together. The formula used is |
584 | | // round(2^|bits|/kPrime*x) mod 2^|bits|. |
585 | | // Uses Barrett reduction to achieve constant time. Since we need both the |
586 | | // remainder (for rounding) and the quotient (as the result), we cannot use |
587 | | // |reduce| here, but need to do the Barrett reduction directly. |
588 | 236k | static uint16_t compress(uint16_t x, int bits) { |
589 | 236k | uint32_t shifted = (uint32_t)x << bits; |
590 | 236k | uint64_t product = (uint64_t)shifted * kBarrettMultiplier; |
591 | 236k | uint32_t quotient = (uint32_t)(product >> kBarrettShift); |
592 | 236k | uint32_t remainder = shifted - quotient * kPrime; |
593 | | |
594 | | // Adjust the quotient to round correctly: |
595 | | // 0 <= remainder <= kHalfPrime round to 0 |
596 | | // kHalfPrime < remainder <= kPrime + kHalfPrime round to 1 |
597 | | // kPrime + kHalfPrime < remainder < 2 * kPrime round to 2 |
598 | 236k | declassify_assert(remainder < 2u * kPrime); |
599 | 236k | quotient += 1 & constant_time_lt_w(kHalfPrime, remainder); |
600 | 236k | quotient += 1 & constant_time_lt_w(kPrime + kHalfPrime, remainder); |
601 | 236k | return quotient & ((1 << bits) - 1); |
602 | 236k | } |
603 | | |
604 | | // Decompresses |x| by using an equi-distant representative. The formula is |
605 | | // round(kPrime/2^|bits|*x). Note that 2^|bits| being the divisor allows us to |
606 | | // implement this logic using only bit operations. |
607 | 79.3k | uint16_t decompress(uint16_t x, int bits) { |
608 | 79.3k | uint32_t product = (uint32_t)x * kPrime; |
609 | 79.3k | uint32_t power = 1 << bits; |
610 | | // This is |product| % power, since |power| is a power of 2. |
611 | 79.3k | uint32_t remainder = product & (power - 1); |
612 | | // This is |product| / power, since |power| is a power of 2. |
613 | 79.3k | uint32_t lower = product >> bits; |
614 | | // The rounding logic works since the first half of numbers mod |power| have a |
615 | | // 0 as first bit, and the second half has a 1 as first bit, since |power| is |
616 | | // a power of 2. As a 12 bit number, |remainder| is always positive, so we |
617 | | // will shift in 0s for a right shift. |
618 | 79.3k | return lower + (remainder >> (bits - 1)); |
619 | 79.3k | } |
620 | | |
621 | 925 | static void scalar_compress(scalar *s, int bits) { |
622 | 237k | for (int i = 0; i < DEGREE; i++) { |
623 | 236k | s->c[i] = compress(s->c[i], bits); |
624 | 236k | } |
625 | 925 | } |
626 | | |
627 | 310 | static void scalar_decompress(scalar *s, int bits) { |
628 | 79.6k | for (int i = 0; i < DEGREE; i++) { |
629 | 79.3k | s->c[i] = decompress(s->c[i], bits); |
630 | 79.3k | } |
631 | 310 | } |
632 | | |
633 | | template <int RANK> |
634 | 226 | void vector_compress(vector<RANK> *a, int bits) { |
635 | 904 | for (int i = 0; i < RANK; i++) { |
636 | 678 | scalar_compress(&a->v[i], bits); |
637 | 678 | } |
638 | 226 | } bcm.cc:void mlkem::(anonymous namespace)::vector_compress<3>(mlkem::(anonymous namespace)::vector<3>*, int) Line | Count | Source | 634 | 226 | void vector_compress(vector<RANK> *a, int bits) { | 635 | 904 | for (int i = 0; i < RANK; i++) { | 636 | 678 | scalar_compress(&a->v[i], bits); | 637 | 678 | } | 638 | 226 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::vector_compress<4>(mlkem::(anonymous namespace)::vector<4>*, int) |
639 | | |
640 | | template <int RANK> |
641 | 21 | void vector_decompress(vector<RANK> *a, int bits) { |
642 | 84 | for (int i = 0; i < RANK; i++) { |
643 | 63 | scalar_decompress(&a->v[i], bits); |
644 | 63 | } |
645 | 21 | } bcm.cc:void mlkem::(anonymous namespace)::vector_decompress<3>(mlkem::(anonymous namespace)::vector<3>*, int) Line | Count | Source | 641 | 21 | void vector_decompress(vector<RANK> *a, int bits) { | 642 | 84 | for (int i = 0; i < RANK; i++) { | 643 | 63 | scalar_decompress(&a->v[i], bits); | 644 | 63 | } | 645 | 21 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::vector_decompress<4>(mlkem::(anonymous namespace)::vector<4>*, int) |
646 | | |
647 | | template <int RANK> |
648 | | struct public_key { |
649 | | vector<RANK> t; |
650 | | uint8_t rho[32]; |
651 | | uint8_t public_key_hash[32]; |
652 | | matrix<RANK> m; |
653 | | }; |
654 | | |
655 | | template <int RANK> |
656 | | struct private_key { |
657 | | struct public_key<RANK> pub; |
658 | | vector<RANK> s; |
659 | | uint8_t fo_failure_secret[32]; |
660 | | }; |
661 | | |
662 | | template <int RANK> |
663 | | static void decrypt_cpa( |
664 | | uint8_t out[32], const struct private_key<RANK> *priv, |
665 | 21 | const uint8_t ciphertext[BCM_MLKEM768_CIPHERTEXT_BYTES]) { |
666 | 21 | constexpr int du = RANK == RANK768 ? kDU768 : kDU1024; |
667 | 21 | constexpr int dv = RANK == RANK768 ? kDV768 : kDV1024; |
668 | | |
669 | 21 | vector<RANK> u; |
670 | 21 | vector_decode(&u, ciphertext, du); |
671 | 21 | vector_decompress(&u, du); |
672 | 21 | vector_ntt(&u); |
673 | 21 | scalar v; |
674 | 21 | scalar_decode(&v, ciphertext + compressed_vector_size(RANK), dv); |
675 | 21 | scalar_decompress(&v, dv); |
676 | 21 | scalar mask; |
677 | 21 | scalar_inner_product(&mask, &priv->s, &u); |
678 | 21 | scalar_inverse_ntt(&mask); |
679 | 21 | scalar_sub(&v, &mask); |
680 | 21 | scalar_compress(&v, 1); |
681 | 21 | scalar_encode_1(out, &v); |
682 | 21 | } bcm.cc:void mlkem::(anonymous namespace)::decrypt_cpa<3>(unsigned char*, mlkem::(anonymous namespace)::private_key<3> const*, unsigned char const*) Line | Count | Source | 665 | 21 | const uint8_t ciphertext[BCM_MLKEM768_CIPHERTEXT_BYTES]) { | 666 | 21 | constexpr int du = RANK == RANK768 ? kDU768 : kDU1024; | 667 | 21 | constexpr int dv = RANK == RANK768 ? kDV768 : kDV1024; | 668 | | | 669 | 21 | vector<RANK> u; | 670 | 21 | vector_decode(&u, ciphertext, du); | 671 | 21 | vector_decompress(&u, du); | 672 | 21 | vector_ntt(&u); | 673 | 21 | scalar v; | 674 | 21 | scalar_decode(&v, ciphertext + compressed_vector_size(RANK), dv); | 675 | 21 | scalar_decompress(&v, dv); | 676 | 21 | scalar mask; | 677 | 21 | scalar_inner_product(&mask, &priv->s, &u); | 678 | 21 | scalar_inverse_ntt(&mask); | 679 | 21 | scalar_sub(&v, &mask); | 680 | 21 | scalar_compress(&v, 1); | 681 | 21 | scalar_encode_1(out, &v); | 682 | 21 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::decrypt_cpa<4>(unsigned char*, mlkem::(anonymous namespace)::private_key<4> const*, unsigned char const*) |
683 | | |
684 | | template <int RANK> |
685 | | static bcm_status mlkem_marshal_public_key(CBB *out, |
686 | 67.2k | const struct public_key<RANK> *pub) { |
687 | 67.2k | uint8_t *vector_output; |
688 | 67.2k | if (!CBB_add_space(out, &vector_output, encoded_vector_size(RANK))) { |
689 | 0 | return bcm_status::failure; |
690 | 0 | } |
691 | 67.2k | vector_encode(vector_output, &pub->t, kLog2Prime); |
692 | 67.2k | if (!CBB_add_bytes(out, pub->rho, sizeof(pub->rho))) { |
693 | 0 | return bcm_status::failure; |
694 | 0 | } |
695 | 67.2k | return bcm_status::approved; |
696 | 67.2k | } bcm.cc:bcm_status_t mlkem::(anonymous namespace)::mlkem_marshal_public_key<3>(cbb_st*, mlkem::(anonymous namespace)::public_key<3> const*) Line | Count | Source | 686 | 67.2k | const struct public_key<RANK> *pub) { | 687 | 67.2k | uint8_t *vector_output; | 688 | 67.2k | if (!CBB_add_space(out, &vector_output, encoded_vector_size(RANK))) { | 689 | 0 | return bcm_status::failure; | 690 | 0 | } | 691 | 67.2k | vector_encode(vector_output, &pub->t, kLog2Prime); | 692 | 67.2k | if (!CBB_add_bytes(out, pub->rho, sizeof(pub->rho))) { | 693 | 0 | return bcm_status::failure; | 694 | 0 | } | 695 | 67.2k | return bcm_status::approved; | 696 | 67.2k | } |
bcm.cc:bcm_status_t mlkem::(anonymous namespace)::mlkem_marshal_public_key<4>(cbb_st*, mlkem::(anonymous namespace)::public_key<4> const*) Line | Count | Source | 686 | 8 | const struct public_key<RANK> *pub) { | 687 | 8 | uint8_t *vector_output; | 688 | 8 | if (!CBB_add_space(out, &vector_output, encoded_vector_size(RANK))) { | 689 | 0 | return bcm_status::failure; | 690 | 0 | } | 691 | 8 | vector_encode(vector_output, &pub->t, kLog2Prime); | 692 | 8 | if (!CBB_add_bytes(out, pub->rho, sizeof(pub->rho))) { | 693 | 0 | return bcm_status::failure; | 694 | 0 | } | 695 | 8 | return bcm_status::approved; | 696 | 8 | } |
|
697 | | |
698 | | template <int RANK> |
699 | | void mlkem_generate_key_external_seed_no_self_test( |
700 | | uint8_t *out_encoded_public_key, private_key<RANK> *priv, |
701 | 67.2k | const uint8_t seed[BCM_MLKEM_SEED_BYTES]) { |
702 | 67.2k | uint8_t augmented_seed[33]; |
703 | 67.2k | OPENSSL_memcpy(augmented_seed, seed, 32); |
704 | 67.2k | augmented_seed[32] = RANK; |
705 | | |
706 | 67.2k | uint8_t hashed[64]; |
707 | 67.2k | hash_g(hashed, augmented_seed, sizeof(augmented_seed)); |
708 | 67.2k | const uint8_t *const rho = hashed; |
709 | 67.2k | const uint8_t *const sigma = hashed + 32; |
710 | | // rho is public. |
711 | 67.2k | CONSTTIME_DECLASSIFY(rho, 32); |
712 | 67.2k | OPENSSL_memcpy(priv->pub.rho, hashed, sizeof(priv->pub.rho)); |
713 | 67.2k | matrix_expand(&priv->pub.m, rho); |
714 | 67.2k | uint8_t counter = 0; |
715 | 67.2k | vector_generate_secret_eta_2(&priv->s, &counter, sigma); |
716 | 67.2k | vector_ntt(&priv->s); |
717 | 67.2k | vector<RANK> error; |
718 | 67.2k | vector_generate_secret_eta_2(&error, &counter, sigma); |
719 | 67.2k | vector_ntt(&error); |
720 | 67.2k | matrix_mult_transpose(&priv->pub.t, &priv->pub.m, &priv->s); |
721 | 67.2k | vector_add(&priv->pub.t, &error); |
722 | | // t is part of the public key and thus is public. |
723 | 67.2k | CONSTTIME_DECLASSIFY(&priv->pub.t, sizeof(priv->pub.t)); |
724 | | |
725 | 67.2k | CBB cbb; |
726 | 67.2k | CBB_init_fixed(&cbb, out_encoded_public_key, encoded_public_key_size(RANK)); |
727 | 67.2k | if (!bcm_success(mlkem_marshal_public_key(&cbb, &priv->pub))) { |
728 | 0 | abort(); |
729 | 0 | } |
730 | | |
731 | 67.2k | hash_h(priv->pub.public_key_hash, out_encoded_public_key, |
732 | 67.2k | encoded_public_key_size(RANK)); |
733 | 67.2k | OPENSSL_memcpy(priv->fo_failure_secret, seed + 32, 32); |
734 | 67.2k | } bcm.cc:void mlkem::(anonymous namespace)::mlkem_generate_key_external_seed_no_self_test<3>(unsigned char*, mlkem::(anonymous namespace)::private_key<3>*, unsigned char const*) Line | Count | Source | 701 | 67.2k | const uint8_t seed[BCM_MLKEM_SEED_BYTES]) { | 702 | 67.2k | uint8_t augmented_seed[33]; | 703 | 67.2k | OPENSSL_memcpy(augmented_seed, seed, 32); | 704 | 67.2k | augmented_seed[32] = RANK; | 705 | | | 706 | 67.2k | uint8_t hashed[64]; | 707 | 67.2k | hash_g(hashed, augmented_seed, sizeof(augmented_seed)); | 708 | 67.2k | const uint8_t *const rho = hashed; | 709 | 67.2k | const uint8_t *const sigma = hashed + 32; | 710 | | // rho is public. | 711 | 67.2k | CONSTTIME_DECLASSIFY(rho, 32); | 712 | 67.2k | OPENSSL_memcpy(priv->pub.rho, hashed, sizeof(priv->pub.rho)); | 713 | 67.2k | matrix_expand(&priv->pub.m, rho); | 714 | 67.2k | uint8_t counter = 0; | 715 | 67.2k | vector_generate_secret_eta_2(&priv->s, &counter, sigma); | 716 | 67.2k | vector_ntt(&priv->s); | 717 | 67.2k | vector<RANK> error; | 718 | 67.2k | vector_generate_secret_eta_2(&error, &counter, sigma); | 719 | 67.2k | vector_ntt(&error); | 720 | 67.2k | matrix_mult_transpose(&priv->pub.t, &priv->pub.m, &priv->s); | 721 | 67.2k | vector_add(&priv->pub.t, &error); | 722 | | // t is part of the public key and thus is public. | 723 | 67.2k | CONSTTIME_DECLASSIFY(&priv->pub.t, sizeof(priv->pub.t)); | 724 | | | 725 | 67.2k | CBB cbb; | 726 | 67.2k | CBB_init_fixed(&cbb, out_encoded_public_key, encoded_public_key_size(RANK)); | 727 | 67.2k | if (!bcm_success(mlkem_marshal_public_key(&cbb, &priv->pub))) { | 728 | 0 | abort(); | 729 | 0 | } | 730 | | | 731 | 67.2k | hash_h(priv->pub.public_key_hash, out_encoded_public_key, | 732 | 67.2k | encoded_public_key_size(RANK)); | 733 | 67.2k | OPENSSL_memcpy(priv->fo_failure_secret, seed + 32, 32); | 734 | 67.2k | } |
bcm.cc:void mlkem::(anonymous namespace)::mlkem_generate_key_external_seed_no_self_test<4>(unsigned char*, mlkem::(anonymous namespace)::private_key<4>*, unsigned char const*) Line | Count | Source | 701 | 8 | const uint8_t seed[BCM_MLKEM_SEED_BYTES]) { | 702 | 8 | uint8_t augmented_seed[33]; | 703 | 8 | OPENSSL_memcpy(augmented_seed, seed, 32); | 704 | 8 | augmented_seed[32] = RANK; | 705 | | | 706 | 8 | uint8_t hashed[64]; | 707 | 8 | hash_g(hashed, augmented_seed, sizeof(augmented_seed)); | 708 | 8 | const uint8_t *const rho = hashed; | 709 | 8 | const uint8_t *const sigma = hashed + 32; | 710 | | // rho is public. | 711 | 8 | CONSTTIME_DECLASSIFY(rho, 32); | 712 | 8 | OPENSSL_memcpy(priv->pub.rho, hashed, sizeof(priv->pub.rho)); | 713 | 8 | matrix_expand(&priv->pub.m, rho); | 714 | 8 | uint8_t counter = 0; | 715 | 8 | vector_generate_secret_eta_2(&priv->s, &counter, sigma); | 716 | 8 | vector_ntt(&priv->s); | 717 | 8 | vector<RANK> error; | 718 | 8 | vector_generate_secret_eta_2(&error, &counter, sigma); | 719 | 8 | vector_ntt(&error); | 720 | 8 | matrix_mult_transpose(&priv->pub.t, &priv->pub.m, &priv->s); | 721 | 8 | vector_add(&priv->pub.t, &error); | 722 | | // t is part of the public key and thus is public. | 723 | 8 | CONSTTIME_DECLASSIFY(&priv->pub.t, sizeof(priv->pub.t)); | 724 | | | 725 | 8 | CBB cbb; | 726 | 8 | CBB_init_fixed(&cbb, out_encoded_public_key, encoded_public_key_size(RANK)); | 727 | 8 | if (!bcm_success(mlkem_marshal_public_key(&cbb, &priv->pub))) { | 728 | 0 | abort(); | 729 | 0 | } | 730 | | | 731 | 8 | hash_h(priv->pub.public_key_hash, out_encoded_public_key, | 732 | 8 | encoded_public_key_size(RANK)); | 733 | 8 | OPENSSL_memcpy(priv->fo_failure_secret, seed + 32, 32); | 734 | 8 | } |
|
735 | | |
736 | | template <int RANK> |
737 | | void mlkem_generate_key_external_seed( |
738 | | uint8_t *out_encoded_public_key, private_key<RANK> *priv, |
739 | 67.2k | const uint8_t seed[BCM_MLKEM_SEED_BYTES]) { |
740 | 67.2k | fips::ensure_keygen_self_test(); |
741 | 67.2k | mlkem_generate_key_external_seed_no_self_test(out_encoded_public_key, priv, |
742 | 67.2k | seed); |
743 | 67.2k | } bcm.cc:void mlkem::(anonymous namespace)::mlkem_generate_key_external_seed<3>(unsigned char*, mlkem::(anonymous namespace)::private_key<3>*, unsigned char const*) Line | Count | Source | 739 | 67.2k | const uint8_t seed[BCM_MLKEM_SEED_BYTES]) { | 740 | 67.2k | fips::ensure_keygen_self_test(); | 741 | 67.2k | mlkem_generate_key_external_seed_no_self_test(out_encoded_public_key, priv, | 742 | 67.2k | seed); | 743 | 67.2k | } |
bcm.cc:void mlkem::(anonymous namespace)::mlkem_generate_key_external_seed<4>(unsigned char*, mlkem::(anonymous namespace)::private_key<4>*, unsigned char const*) Line | Count | Source | 739 | 8 | const uint8_t seed[BCM_MLKEM_SEED_BYTES]) { | 740 | 8 | fips::ensure_keygen_self_test(); | 741 | 8 | mlkem_generate_key_external_seed_no_self_test(out_encoded_public_key, priv, | 742 | 8 | seed); | 743 | 8 | } |
|
744 | | |
745 | | // Encrypts a message with given randomness to |
746 | | // the ciphertext in |out|. Without applying the Fujisaki-Okamoto transform this |
747 | | // would not result in a CCA secure scheme, since lattice schemes are vulnerable |
748 | | // to decryption failure oracles. |
749 | | template <int RANK> |
750 | | void encrypt_cpa(uint8_t *out, const struct mlkem::public_key<RANK> *pub, |
751 | 226 | const uint8_t message[32], const uint8_t randomness[32]) { |
752 | 226 | constexpr int du = RANK == RANK768 ? mlkem::kDU768 : mlkem::kDU1024; |
753 | 226 | constexpr int dv = RANK == RANK768 ? mlkem::kDV768 : mlkem::kDV1024; |
754 | | |
755 | 226 | uint8_t counter = 0; |
756 | 226 | mlkem::vector<RANK> secret; |
757 | 226 | vector_generate_secret_eta_2(&secret, &counter, randomness); |
758 | 226 | vector_ntt(&secret); |
759 | 226 | mlkem::vector<RANK> error; |
760 | 226 | vector_generate_secret_eta_2(&error, &counter, randomness); |
761 | 226 | uint8_t input[33]; |
762 | 226 | OPENSSL_memcpy(input, randomness, 32); |
763 | 226 | input[32] = counter; |
764 | 226 | mlkem::scalar scalar_error; |
765 | 226 | scalar_centered_binomial_distribution_eta_2_with_prf(&scalar_error, input); |
766 | 226 | mlkem::vector<RANK> u; |
767 | 226 | matrix_mult(&u, &pub->m, &secret); |
768 | 226 | vector_inverse_ntt(&u); |
769 | 226 | vector_add(&u, &error); |
770 | 226 | mlkem::scalar v; |
771 | 226 | scalar_inner_product(&v, &pub->t, &secret); |
772 | 226 | scalar_inverse_ntt(&v); |
773 | 226 | scalar_add(&v, &scalar_error); |
774 | 226 | mlkem::scalar expanded_message; |
775 | 226 | scalar_decode_1(&expanded_message, message); |
776 | 226 | scalar_decompress(&expanded_message, 1); |
777 | 226 | scalar_add(&v, &expanded_message); |
778 | 226 | vector_compress(&u, du); |
779 | 226 | vector_encode(out, &u, du); |
780 | 226 | scalar_compress(&v, dv); |
781 | 226 | scalar_encode(out + mlkem::compressed_vector_size(RANK), &v, dv); |
782 | 226 | } bcm.cc:void mlkem::(anonymous namespace)::encrypt_cpa<3>(unsigned char*, mlkem::(anonymous namespace)::public_key<3> const*, unsigned char const*, unsigned char const*) Line | Count | Source | 751 | 226 | const uint8_t message[32], const uint8_t randomness[32]) { | 752 | 226 | constexpr int du = RANK == RANK768 ? mlkem::kDU768 : mlkem::kDU1024; | 753 | 226 | constexpr int dv = RANK == RANK768 ? mlkem::kDV768 : mlkem::kDV1024; | 754 | | | 755 | 226 | uint8_t counter = 0; | 756 | 226 | mlkem::vector<RANK> secret; | 757 | 226 | vector_generate_secret_eta_2(&secret, &counter, randomness); | 758 | 226 | vector_ntt(&secret); | 759 | 226 | mlkem::vector<RANK> error; | 760 | 226 | vector_generate_secret_eta_2(&error, &counter, randomness); | 761 | 226 | uint8_t input[33]; | 762 | 226 | OPENSSL_memcpy(input, randomness, 32); | 763 | 226 | input[32] = counter; | 764 | 226 | mlkem::scalar scalar_error; | 765 | 226 | scalar_centered_binomial_distribution_eta_2_with_prf(&scalar_error, input); | 766 | 226 | mlkem::vector<RANK> u; | 767 | 226 | matrix_mult(&u, &pub->m, &secret); | 768 | 226 | vector_inverse_ntt(&u); | 769 | 226 | vector_add(&u, &error); | 770 | 226 | mlkem::scalar v; | 771 | 226 | scalar_inner_product(&v, &pub->t, &secret); | 772 | 226 | scalar_inverse_ntt(&v); | 773 | 226 | scalar_add(&v, &scalar_error); | 774 | 226 | mlkem::scalar expanded_message; | 775 | 226 | scalar_decode_1(&expanded_message, message); | 776 | 226 | scalar_decompress(&expanded_message, 1); | 777 | 226 | scalar_add(&v, &expanded_message); | 778 | 226 | vector_compress(&u, du); | 779 | 226 | vector_encode(out, &u, du); | 780 | 226 | scalar_compress(&v, dv); | 781 | 226 | scalar_encode(out + mlkem::compressed_vector_size(RANK), &v, dv); | 782 | 226 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::encrypt_cpa<4>(unsigned char*, mlkem::(anonymous namespace)::public_key<4> const*, unsigned char const*, unsigned char const*) |
783 | | |
784 | | // See section 6.3 |
785 | | template <int RANK> |
786 | | void mlkem_decap_no_self_test( |
787 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
788 | 21 | const uint8_t *ciphertext, const struct private_key<RANK> *priv) { |
789 | 21 | uint8_t decrypted[64]; |
790 | 21 | decrypt_cpa(decrypted, priv, ciphertext); |
791 | 21 | OPENSSL_memcpy(decrypted + 32, priv->pub.public_key_hash, |
792 | 21 | sizeof(decrypted) - 32); |
793 | 21 | uint8_t key_and_randomness[64]; |
794 | 21 | hash_g(key_and_randomness, decrypted, sizeof(decrypted)); |
795 | 21 | constexpr size_t ciphertext_len = ciphertext_size(RANK); |
796 | 21 | uint8_t expected_ciphertext[BCM_MLKEM1024_CIPHERTEXT_BYTES]; |
797 | 21 | static_assert(ciphertext_len <= sizeof(expected_ciphertext)); |
798 | 21 | encrypt_cpa(expected_ciphertext, &priv->pub, decrypted, |
799 | 21 | key_and_randomness + 32); |
800 | | |
801 | 21 | uint8_t failure_key[32]; |
802 | 21 | kdf(failure_key, priv->fo_failure_secret, ciphertext, ciphertext_len); |
803 | | |
804 | 21 | uint8_t mask = constant_time_eq_int_8( |
805 | 21 | CRYPTO_memcmp(ciphertext, expected_ciphertext, ciphertext_len), 0); |
806 | 693 | for (int i = 0; i < BCM_MLKEM_SHARED_SECRET_BYTES; i++) { |
807 | 672 | out_shared_secret[i] = |
808 | 672 | constant_time_select_8(mask, key_and_randomness[i], failure_key[i]); |
809 | 672 | } |
810 | 21 | } bcm.cc:void mlkem::(anonymous namespace)::mlkem_decap_no_self_test<3>(unsigned char*, unsigned char const*, mlkem::(anonymous namespace)::private_key<3> const*) Line | Count | Source | 788 | 21 | const uint8_t *ciphertext, const struct private_key<RANK> *priv) { | 789 | 21 | uint8_t decrypted[64]; | 790 | 21 | decrypt_cpa(decrypted, priv, ciphertext); | 791 | 21 | OPENSSL_memcpy(decrypted + 32, priv->pub.public_key_hash, | 792 | 21 | sizeof(decrypted) - 32); | 793 | 21 | uint8_t key_and_randomness[64]; | 794 | 21 | hash_g(key_and_randomness, decrypted, sizeof(decrypted)); | 795 | 21 | constexpr size_t ciphertext_len = ciphertext_size(RANK); | 796 | 21 | uint8_t expected_ciphertext[BCM_MLKEM1024_CIPHERTEXT_BYTES]; | 797 | 21 | static_assert(ciphertext_len <= sizeof(expected_ciphertext)); | 798 | 21 | encrypt_cpa(expected_ciphertext, &priv->pub, decrypted, | 799 | 21 | key_and_randomness + 32); | 800 | | | 801 | 21 | uint8_t failure_key[32]; | 802 | 21 | kdf(failure_key, priv->fo_failure_secret, ciphertext, ciphertext_len); | 803 | | | 804 | 21 | uint8_t mask = constant_time_eq_int_8( | 805 | 21 | CRYPTO_memcmp(ciphertext, expected_ciphertext, ciphertext_len), 0); | 806 | 693 | for (int i = 0; i < BCM_MLKEM_SHARED_SECRET_BYTES; i++) { | 807 | 672 | out_shared_secret[i] = | 808 | 672 | constant_time_select_8(mask, key_and_randomness[i], failure_key[i]); | 809 | 672 | } | 810 | 21 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::mlkem_decap_no_self_test<4>(unsigned char*, unsigned char const*, mlkem::(anonymous namespace)::private_key<4> const*) |
811 | | |
812 | | template <int RANK> |
813 | | void mlkem_decap(uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
814 | | const uint8_t *ciphertext, |
815 | 21 | const struct private_key<RANK> *priv) { |
816 | 21 | fips::ensure_decap_self_test(); |
817 | 21 | mlkem_decap_no_self_test(out_shared_secret, ciphertext, priv); |
818 | 21 | } bcm.cc:void mlkem::(anonymous namespace)::mlkem_decap<3>(unsigned char*, unsigned char const*, mlkem::(anonymous namespace)::private_key<3> const*) Line | Count | Source | 815 | 21 | const struct private_key<RANK> *priv) { | 816 | 21 | fips::ensure_decap_self_test(); | 817 | 21 | mlkem_decap_no_self_test(out_shared_secret, ciphertext, priv); | 818 | 21 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::mlkem_decap<4>(unsigned char*, unsigned char const*, mlkem::(anonymous namespace)::private_key<4> const*) |
819 | | |
820 | | // mlkem_parse_public_key_no_hash parses |in| into |pub| but doesn't calculate |
821 | | // the value of |pub->public_key_hash|. |
822 | | template <int RANK> |
823 | 281 | int mlkem_parse_public_key_no_hash(struct public_key<RANK> *pub, CBS *in) { |
824 | 281 | CBS t_bytes; |
825 | 281 | if (!CBS_get_bytes(in, &t_bytes, encoded_vector_size(RANK)) || |
826 | 281 | !vector_decode(&pub->t, CBS_data(&t_bytes), kLog2Prime) || |
827 | 281 | !CBS_copy_bytes(in, pub->rho, sizeof(pub->rho))) { |
828 | 44 | return 0; |
829 | 44 | } |
830 | 237 | matrix_expand(&pub->m, pub->rho); |
831 | 237 | return 1; |
832 | 281 | } bcm.cc:int mlkem::(anonymous namespace)::mlkem_parse_public_key_no_hash<3>(mlkem::(anonymous namespace)::public_key<3>*, cbs_st*) Line | Count | Source | 823 | 281 | int mlkem_parse_public_key_no_hash(struct public_key<RANK> *pub, CBS *in) { | 824 | 281 | CBS t_bytes; | 825 | 281 | if (!CBS_get_bytes(in, &t_bytes, encoded_vector_size(RANK)) || | 826 | 281 | !vector_decode(&pub->t, CBS_data(&t_bytes), kLog2Prime) || | 827 | 281 | !CBS_copy_bytes(in, pub->rho, sizeof(pub->rho))) { | 828 | 44 | return 0; | 829 | 44 | } | 830 | 237 | matrix_expand(&pub->m, pub->rho); | 831 | 237 | return 1; | 832 | 281 | } |
Unexecuted instantiation: bcm.cc:int mlkem::(anonymous namespace)::mlkem_parse_public_key_no_hash<4>(mlkem::(anonymous namespace)::public_key<4>*, cbs_st*) |
833 | | |
834 | | template <int RANK> |
835 | 281 | int mlkem_parse_public_key(struct public_key<RANK> *pub, CBS *in) { |
836 | 281 | CBS orig_in = *in; |
837 | 281 | if (!mlkem_parse_public_key_no_hash(pub, in) || // |
838 | 281 | CBS_len(in) != 0) { |
839 | 44 | return 0; |
840 | 44 | } |
841 | 237 | hash_h(pub->public_key_hash, CBS_data(&orig_in), CBS_len(&orig_in)); |
842 | 237 | return 1; |
843 | 281 | } bcm.cc:int mlkem::(anonymous namespace)::mlkem_parse_public_key<3>(mlkem::(anonymous namespace)::public_key<3>*, cbs_st*) Line | Count | Source | 835 | 281 | int mlkem_parse_public_key(struct public_key<RANK> *pub, CBS *in) { | 836 | 281 | CBS orig_in = *in; | 837 | 281 | if (!mlkem_parse_public_key_no_hash(pub, in) || // | 838 | 281 | CBS_len(in) != 0) { | 839 | 44 | return 0; | 840 | 44 | } | 841 | 237 | hash_h(pub->public_key_hash, CBS_data(&orig_in), CBS_len(&orig_in)); | 842 | 237 | return 1; | 843 | 281 | } |
Unexecuted instantiation: bcm.cc:int mlkem::(anonymous namespace)::mlkem_parse_public_key<4>(mlkem::(anonymous namespace)::public_key<4>*, cbs_st*) |
844 | | |
845 | | template <int RANK> |
846 | 0 | int mlkem_parse_private_key(struct private_key<RANK> *priv, CBS *in) { |
847 | 0 | CBS s_bytes; |
848 | 0 | if (!CBS_get_bytes(in, &s_bytes, encoded_vector_size(RANK)) || |
849 | 0 | !vector_decode(&priv->s, CBS_data(&s_bytes), kLog2Prime) || |
850 | 0 | !mlkem_parse_public_key_no_hash(&priv->pub, in) || |
851 | 0 | !CBS_copy_bytes(in, priv->pub.public_key_hash, |
852 | 0 | sizeof(priv->pub.public_key_hash)) || |
853 | 0 | !CBS_copy_bytes(in, priv->fo_failure_secret, |
854 | 0 | sizeof(priv->fo_failure_secret)) || |
855 | 0 | CBS_len(in) != 0) { |
856 | 0 | return 0; |
857 | 0 | } |
858 | 0 | return 1; |
859 | 0 | } Unexecuted instantiation: bcm.cc:int mlkem::(anonymous namespace)::mlkem_parse_private_key<3>(mlkem::(anonymous namespace)::private_key<3>*, cbs_st*) Unexecuted instantiation: bcm.cc:int mlkem::(anonymous namespace)::mlkem_parse_private_key<4>(mlkem::(anonymous namespace)::private_key<4>*, cbs_st*) |
860 | | |
861 | | template <int RANK> |
862 | 0 | int mlkem_marshal_private_key(CBB *out, const struct private_key<RANK> *priv) { |
863 | 0 | uint8_t *s_output; |
864 | 0 | if (!CBB_add_space(out, &s_output, encoded_vector_size(RANK))) { |
865 | 0 | return 0; |
866 | 0 | } |
867 | 0 | vector_encode(s_output, &priv->s, kLog2Prime); |
868 | 0 | if (!bcm_success(mlkem_marshal_public_key(out, &priv->pub)) || |
869 | 0 | !CBB_add_bytes(out, priv->pub.public_key_hash, |
870 | 0 | sizeof(priv->pub.public_key_hash)) || |
871 | 0 | !CBB_add_bytes(out, priv->fo_failure_secret, |
872 | 0 | sizeof(priv->fo_failure_secret))) { |
873 | 0 | return 0; |
874 | 0 | } |
875 | 0 | return 1; |
876 | 0 | } Unexecuted instantiation: bcm.cc:int mlkem::(anonymous namespace)::mlkem_marshal_private_key<3>(cbb_st*, mlkem::(anonymous namespace)::private_key<3> const*) Unexecuted instantiation: bcm.cc:int mlkem::(anonymous namespace)::mlkem_marshal_private_key<4>(cbb_st*, mlkem::(anonymous namespace)::private_key<4> const*) |
877 | | |
878 | | struct public_key<RANK768> *public_key_768_from_external( |
879 | 486 | const struct BCM_mlkem768_public_key *external) { |
880 | 486 | static_assert(sizeof(struct BCM_mlkem768_public_key) >= |
881 | 486 | sizeof(struct public_key<RANK768>), |
882 | 486 | "MLKEM public key is too small"); |
883 | 486 | static_assert(alignof(struct BCM_mlkem768_public_key) >= |
884 | 486 | alignof(struct public_key<RANK768>), |
885 | 486 | "MLKEM public key alignment incorrect"); |
886 | 486 | return (struct public_key<RANK768> *)external; |
887 | 486 | } |
888 | | |
889 | | static struct public_key<RANK1024> *public_key_1024_from_external( |
890 | 0 | const struct BCM_mlkem1024_public_key *external) { |
891 | 0 | static_assert(sizeof(struct BCM_mlkem1024_public_key) >= |
892 | 0 | sizeof(struct public_key<RANK1024>), |
893 | 0 | "MLKEM1024 public key is too small"); |
894 | 0 | static_assert(alignof(struct BCM_mlkem1024_public_key) >= |
895 | 0 | alignof(struct public_key<RANK1024>), |
896 | 0 | "MLKEM1024 public key alignment incorrect"); |
897 | 0 | return (struct public_key<RANK1024> *)external; |
898 | 0 | } |
899 | | |
900 | | struct private_key<RANK768> *private_key_768_from_external( |
901 | 67.3k | const struct BCM_mlkem768_private_key *external) { |
902 | 67.3k | static_assert(sizeof(struct BCM_mlkem768_private_key) >= |
903 | 67.3k | sizeof(struct private_key<RANK768>), |
904 | 67.3k | "MLKEM private key too small"); |
905 | 67.3k | static_assert(alignof(struct BCM_mlkem768_private_key) >= |
906 | 67.3k | alignof(struct private_key<RANK768>), |
907 | 67.3k | "MLKEM private key alignment incorrect"); |
908 | 67.3k | return (struct private_key<RANK768> *)external; |
909 | 67.3k | } |
910 | | |
911 | | struct private_key<RANK1024> *private_key_1024_from_external( |
912 | 8 | const struct BCM_mlkem1024_private_key *external) { |
913 | 8 | static_assert(sizeof(struct BCM_mlkem1024_private_key) >= |
914 | 8 | sizeof(struct private_key<RANK1024>), |
915 | 8 | "MLKEM1024 private key too small"); |
916 | 8 | static_assert(alignof(struct BCM_mlkem1024_private_key) >= |
917 | 8 | alignof(struct private_key<RANK1024>), |
918 | 8 | "MLKEM1024 private key alignment incorrect"); |
919 | 8 | return (struct private_key<RANK1024> *)external; |
920 | 8 | } |
921 | | |
922 | | // See section 6.2. |
923 | | template <int RANK> |
924 | | void mlkem_encap_external_entropy_no_self_test( |
925 | | uint8_t *out_ciphertext, |
926 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
927 | | const struct mlkem::public_key<RANK> *pub, |
928 | 205 | const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]) { |
929 | 205 | uint8_t input[64]; |
930 | 205 | OPENSSL_memcpy(input, entropy, BCM_MLKEM_ENCAP_ENTROPY); |
931 | 205 | OPENSSL_memcpy(input + BCM_MLKEM_ENCAP_ENTROPY, pub->public_key_hash, |
932 | 205 | sizeof(input) - BCM_MLKEM_ENCAP_ENTROPY); |
933 | 205 | uint8_t key_and_randomness[64]; |
934 | 205 | mlkem::hash_g(key_and_randomness, input, sizeof(input)); |
935 | 205 | encrypt_cpa(out_ciphertext, pub, entropy, key_and_randomness + 32); |
936 | | // The ciphertext is public. |
937 | 205 | CONSTTIME_DECLASSIFY(out_ciphertext, mlkem::ciphertext_size(RANK)); |
938 | 205 | static_assert(BCM_MLKEM_SHARED_SECRET_BYTES == 32); |
939 | 205 | memcpy(out_shared_secret, key_and_randomness, 32); |
940 | 205 | } bcm.cc:void mlkem::(anonymous namespace)::mlkem_encap_external_entropy_no_self_test<3>(unsigned char*, unsigned char*, mlkem::(anonymous namespace)::public_key<3> const*, unsigned char const*) Line | Count | Source | 928 | 205 | const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]) { | 929 | 205 | uint8_t input[64]; | 930 | 205 | OPENSSL_memcpy(input, entropy, BCM_MLKEM_ENCAP_ENTROPY); | 931 | 205 | OPENSSL_memcpy(input + BCM_MLKEM_ENCAP_ENTROPY, pub->public_key_hash, | 932 | 205 | sizeof(input) - BCM_MLKEM_ENCAP_ENTROPY); | 933 | 205 | uint8_t key_and_randomness[64]; | 934 | 205 | mlkem::hash_g(key_and_randomness, input, sizeof(input)); | 935 | 205 | encrypt_cpa(out_ciphertext, pub, entropy, key_and_randomness + 32); | 936 | | // The ciphertext is public. | 937 | 205 | CONSTTIME_DECLASSIFY(out_ciphertext, mlkem::ciphertext_size(RANK)); | 938 | 205 | static_assert(BCM_MLKEM_SHARED_SECRET_BYTES == 32); | 939 | 205 | memcpy(out_shared_secret, key_and_randomness, 32); | 940 | 205 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::mlkem_encap_external_entropy_no_self_test<4>(unsigned char*, unsigned char*, mlkem::(anonymous namespace)::public_key<4> const*, unsigned char const*) |
941 | | |
942 | | template <int RANK> |
943 | | void mlkem_encap_external_entropy( |
944 | | uint8_t *out_ciphertext, |
945 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
946 | | const struct mlkem::public_key<RANK> *pub, |
947 | 205 | const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]) { |
948 | 205 | fips::ensure_encap_self_test(); |
949 | 205 | mlkem_encap_external_entropy_no_self_test(out_ciphertext, out_shared_secret, |
950 | 205 | pub, entropy); |
951 | 205 | } bcm.cc:void mlkem::(anonymous namespace)::mlkem_encap_external_entropy<3>(unsigned char*, unsigned char*, mlkem::(anonymous namespace)::public_key<3> const*, unsigned char const*) Line | Count | Source | 947 | 205 | const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]) { | 948 | 205 | fips::ensure_encap_self_test(); | 949 | 205 | mlkem_encap_external_entropy_no_self_test(out_ciphertext, out_shared_secret, | 950 | 205 | pub, entropy); | 951 | 205 | } |
Unexecuted instantiation: bcm.cc:void mlkem::(anonymous namespace)::mlkem_encap_external_entropy<4>(unsigned char*, unsigned char*, mlkem::(anonymous namespace)::public_key<4> const*, unsigned char const*) |
952 | | |
953 | | namespace fips { |
954 | | |
955 | | #include "fips_known_values.inc" |
956 | | |
957 | 0 | static int keygen_self_test() { |
958 | 0 | uint8_t pub_key[BCM_MLKEM768_PUBLIC_KEY_BYTES]; |
959 | 0 | private_key<RANK768> priv; |
960 | 0 | static_assert(sizeof(kTestEntropy) >= BCM_MLKEM_SEED_BYTES); |
961 | 0 | mlkem_generate_key_external_seed_no_self_test(pub_key, &priv, kTestEntropy); |
962 | 0 | CBB cbb; |
963 | 0 | constexpr size_t kMarshaledPrivateKeySize = 2400; |
964 | 0 | uint8_t priv_bytes[kMarshaledPrivateKeySize]; |
965 | 0 | CBB_init_fixed(&cbb, priv_bytes, sizeof(priv_bytes)); |
966 | 0 | static_assert(sizeof(kExpectedPrivateKeyBytes) == kMarshaledPrivateKeySize); |
967 | 0 | static_assert(sizeof(kExpectedPublicKeyBytes) == sizeof(pub_key)); |
968 | 0 | if (!mlkem_marshal_private_key(&cbb, &priv) || |
969 | 0 | !BORINGSSL_check_test(kExpectedPrivateKeyBytes, priv_bytes, |
970 | 0 | sizeof(priv_bytes), "ML-KEM keygen private key") || |
971 | 0 | !BORINGSSL_check_test(kExpectedPublicKeyBytes, pub_key, sizeof(pub_key), |
972 | 0 | "ML-KEM keygen public key")) { |
973 | 0 | return 0; |
974 | 0 | } |
975 | 0 | return 1; |
976 | 0 | } |
977 | | |
978 | 0 | static int encap_self_test() { |
979 | 0 | CBS cbs; |
980 | 0 | CBS_init(&cbs, kExpectedPublicKeyBytes, sizeof(kExpectedPublicKeyBytes)); |
981 | 0 | public_key<RANK768> pub; |
982 | 0 | if (!mlkem_parse_public_key(&pub, &cbs)) { |
983 | 0 | return 0; |
984 | 0 | } |
985 | 0 | uint8_t ciphertext[BCM_MLKEM768_CIPHERTEXT_BYTES]; |
986 | 0 | uint8_t shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES]; |
987 | 0 | static_assert(sizeof(kTestEntropy) >= BCM_MLKEM_ENCAP_ENTROPY); |
988 | 0 | mlkem_encap_external_entropy_no_self_test(ciphertext, shared_secret, &pub, |
989 | 0 | kTestEntropy); |
990 | 0 | if (!BORINGSSL_check_test(ciphertext, kExpectedCiphertext, sizeof(ciphertext), |
991 | 0 | "ML-KEM encap ciphertext") || |
992 | 0 | !BORINGSSL_check_test(kExpectedSharedSecret, shared_secret, |
993 | 0 | sizeof(kExpectedSharedSecret), |
994 | 0 | "ML-KEM encap shared secret")) { |
995 | 0 | return 0; |
996 | 0 | } |
997 | 0 | return 1; |
998 | 0 | } |
999 | | |
1000 | 0 | static int decap_self_test() { |
1001 | 0 | CBS cbs; |
1002 | 0 | CBS_init(&cbs, kExpectedPrivateKeyBytes, sizeof(kExpectedPrivateKeyBytes)); |
1003 | 0 | private_key<RANK768> priv; |
1004 | 0 | if (!mlkem_parse_private_key(&priv, &cbs)) { |
1005 | 0 | return 0; |
1006 | 0 | } |
1007 | 0 | uint8_t shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES]; |
1008 | 0 | mlkem_decap_no_self_test(shared_secret, kExpectedCiphertext, &priv); |
1009 | 0 | static_assert(sizeof(kExpectedSharedSecret) == sizeof(shared_secret)); |
1010 | 0 | if (!BORINGSSL_check_test(kExpectedSharedSecret, shared_secret, |
1011 | 0 | sizeof(shared_secret), |
1012 | 0 | "ML-KEM decap shared secret")) { |
1013 | 0 | return 0; |
1014 | 0 | } |
1015 | | |
1016 | 0 | uint8_t implicit_rejection_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES]; |
1017 | 0 | static_assert(sizeof(kExpectedPrivateKeyBytes) >= |
1018 | 0 | sizeof(kExpectedCiphertext)); |
1019 | 0 | mlkem_decap_no_self_test(implicit_rejection_shared_secret, |
1020 | 0 | kExpectedPrivateKeyBytes, &priv); |
1021 | 0 | static_assert(sizeof(kExpectedImplicitRejectionSharedSecret) == |
1022 | 0 | sizeof(implicit_rejection_shared_secret)); |
1023 | 0 | if (!BORINGSSL_check_test(kExpectedImplicitRejectionSharedSecret, |
1024 | 0 | implicit_rejection_shared_secret, |
1025 | 0 | sizeof(implicit_rejection_shared_secret), |
1026 | 0 | "ML-KEM decap implicit rejection shared secret")) { |
1027 | 0 | return 0; |
1028 | 0 | } |
1029 | 0 | return 1; |
1030 | 0 | } |
1031 | | |
1032 | | #if defined(BORINGSSL_FIPS) |
1033 | | |
1034 | | DEFINE_STATIC_ONCE(g_mlkem_keygen_self_test_once) |
1035 | | |
1036 | | void ensure_keygen_self_test(void) { |
1037 | | CRYPTO_once(g_mlkem_keygen_self_test_once_bss_get(), []() { |
1038 | | if (!keygen_self_test()) { |
1039 | | BORINGSSL_FIPS_abort(); |
1040 | | } |
1041 | | }); |
1042 | | } |
1043 | | |
1044 | | DEFINE_STATIC_ONCE(g_mlkem_encap_self_test_once) |
1045 | | |
1046 | | void ensure_encap_self_test(void) { |
1047 | | CRYPTO_once(g_mlkem_encap_self_test_once_bss_get(), []() { |
1048 | | if (!encap_self_test()) { |
1049 | | BORINGSSL_FIPS_abort(); |
1050 | | } |
1051 | | }); |
1052 | | } |
1053 | | |
1054 | | DEFINE_STATIC_ONCE(g_mlkem_decap_self_test_once) |
1055 | | |
1056 | | void ensure_decap_self_test(void) { |
1057 | | CRYPTO_once(g_mlkem_decap_self_test_once_bss_get(), []() { |
1058 | | if (!decap_self_test()) { |
1059 | | BORINGSSL_FIPS_abort(); |
1060 | | } |
1061 | | }); |
1062 | | } |
1063 | | |
1064 | | #else |
1065 | | |
1066 | 67.2k | void ensure_keygen_self_test(void) {} |
1067 | 205 | void ensure_encap_self_test(void) {} |
1068 | 21 | void ensure_decap_self_test(void) {} |
1069 | | |
1070 | | #endif |
1071 | | } // namespace fips |
1072 | | |
1073 | | } // namespace |
1074 | | } // namespace mlkem |
1075 | | |
1076 | | bcm_status BCM_mlkem768_check_fips( |
1077 | 0 | const struct BCM_mlkem768_private_key *private_key) { |
1078 | 0 | mlkem::private_key<RANK768> *priv = |
1079 | 0 | mlkem::private_key_768_from_external(private_key); |
1080 | |
|
1081 | 0 | const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY] = {1, 2, 3, 4}; |
1082 | 0 | uint8_t ciphertext[BCM_MLKEM768_CIPHERTEXT_BYTES]; |
1083 | 0 | uint8_t shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES]; |
1084 | 0 | mlkem_encap_external_entropy_no_self_test(ciphertext, shared_secret, |
1085 | 0 | &priv->pub, entropy); |
1086 | |
|
1087 | 0 | if (boringssl_fips_break_test("MLKEM_PWCT")) { |
1088 | 0 | shared_secret[0] ^= 1; |
1089 | 0 | } |
1090 | |
|
1091 | 0 | uint8_t shared_secret2[BCM_MLKEM_SHARED_SECRET_BYTES]; |
1092 | 0 | mlkem::mlkem_decap_no_self_test(shared_secret2, ciphertext, priv); |
1093 | 0 | if (CRYPTO_memcmp(shared_secret, shared_secret2, sizeof(shared_secret)) != |
1094 | 0 | 0) { |
1095 | 0 | return bcm_status::failure; |
1096 | 0 | } |
1097 | 0 | return bcm_status::approved; |
1098 | 0 | } |
1099 | | |
1100 | | bcm_status BCM_mlkem768_generate_key_fips( |
1101 | | uint8_t out_encoded_public_key[BCM_MLKEM768_PUBLIC_KEY_BYTES], |
1102 | | uint8_t optional_out_seed[BCM_MLKEM_SEED_BYTES], |
1103 | 0 | struct BCM_mlkem768_private_key *out_private_key) { |
1104 | 0 | if (out_encoded_public_key == nullptr || out_private_key == nullptr) { |
1105 | 0 | return bcm_status::failure; |
1106 | 0 | } |
1107 | 0 | BCM_mlkem768_generate_key(out_encoded_public_key, optional_out_seed, |
1108 | 0 | out_private_key); |
1109 | 0 | return BCM_mlkem768_check_fips(out_private_key); |
1110 | 0 | } |
1111 | | |
1112 | | bcm_infallible BCM_mlkem768_generate_key( |
1113 | | uint8_t out_encoded_public_key[BCM_MLKEM768_PUBLIC_KEY_BYTES], |
1114 | | uint8_t optional_out_seed[BCM_MLKEM_SEED_BYTES], |
1115 | 67.2k | struct BCM_mlkem768_private_key *out_private_key) { |
1116 | 67.2k | uint8_t seed[BCM_MLKEM_SEED_BYTES]; |
1117 | 67.2k | BCM_rand_bytes(seed, sizeof(seed)); |
1118 | 67.2k | CONSTTIME_SECRET(seed, sizeof(seed)); |
1119 | 67.2k | if (optional_out_seed) { |
1120 | 0 | OPENSSL_memcpy(optional_out_seed, seed, sizeof(seed)); |
1121 | 0 | } |
1122 | 67.2k | BCM_mlkem768_generate_key_external_seed(out_encoded_public_key, |
1123 | 67.2k | out_private_key, seed); |
1124 | 67.2k | return bcm_infallible::not_approved; |
1125 | 67.2k | } |
1126 | | |
1127 | | bcm_status BCM_mlkem768_private_key_from_seed( |
1128 | | struct BCM_mlkem768_private_key *out_private_key, const uint8_t *seed, |
1129 | 0 | size_t seed_len) { |
1130 | 0 | if (seed_len != BCM_MLKEM_SEED_BYTES) { |
1131 | 0 | return bcm_status::failure; |
1132 | 0 | } |
1133 | | |
1134 | 0 | uint8_t public_key_bytes[BCM_MLKEM768_PUBLIC_KEY_BYTES]; |
1135 | 0 | BCM_mlkem768_generate_key_external_seed(public_key_bytes, out_private_key, |
1136 | 0 | seed); |
1137 | 0 | return bcm_status::not_approved; |
1138 | 0 | } |
1139 | | |
1140 | | bcm_status BCM_mlkem1024_check_fips( |
1141 | 0 | const struct BCM_mlkem1024_private_key *private_key) { |
1142 | 0 | mlkem::private_key<RANK1024> *priv = |
1143 | 0 | mlkem::private_key_1024_from_external(private_key); |
1144 | |
|
1145 | 0 | const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY] = {1, 2, 3, 4}; |
1146 | 0 | uint8_t ciphertext[BCM_MLKEM1024_CIPHERTEXT_BYTES]; |
1147 | 0 | uint8_t shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES]; |
1148 | 0 | mlkem_encap_external_entropy_no_self_test(ciphertext, shared_secret, |
1149 | 0 | &priv->pub, entropy); |
1150 | |
|
1151 | 0 | if (boringssl_fips_break_test("MLKEM_PWCT")) { |
1152 | 0 | shared_secret[0] ^= 1; |
1153 | 0 | } |
1154 | |
|
1155 | 0 | uint8_t shared_secret2[BCM_MLKEM_SHARED_SECRET_BYTES]; |
1156 | 0 | mlkem::mlkem_decap_no_self_test(shared_secret2, ciphertext, priv); |
1157 | 0 | if (CRYPTO_memcmp(shared_secret, shared_secret2, sizeof(shared_secret)) != |
1158 | 0 | 0) { |
1159 | 0 | return bcm_status::failure; |
1160 | 0 | } |
1161 | 0 | return bcm_status::approved; |
1162 | 0 | } |
1163 | | |
1164 | | bcm_status BCM_mlkem1024_generate_key_fips( |
1165 | | uint8_t out_encoded_public_key[BCM_MLKEM1024_PUBLIC_KEY_BYTES], |
1166 | | uint8_t optional_out_seed[BCM_MLKEM_SEED_BYTES], |
1167 | 0 | struct BCM_mlkem1024_private_key *out_private_key) { |
1168 | 0 | if (out_encoded_public_key == nullptr || out_private_key == nullptr) { |
1169 | 0 | return bcm_status::failure; |
1170 | 0 | } |
1171 | 0 | BCM_mlkem1024_generate_key(out_encoded_public_key, optional_out_seed, |
1172 | 0 | out_private_key); |
1173 | 0 | return BCM_mlkem1024_check_fips(out_private_key); |
1174 | 0 | } |
1175 | | |
1176 | | bcm_infallible BCM_mlkem1024_generate_key( |
1177 | | uint8_t out_encoded_public_key[BCM_MLKEM1024_PUBLIC_KEY_BYTES], |
1178 | | uint8_t optional_out_seed[BCM_MLKEM_SEED_BYTES], |
1179 | 8 | struct BCM_mlkem1024_private_key *out_private_key) { |
1180 | 8 | uint8_t seed[BCM_MLKEM_SEED_BYTES]; |
1181 | 8 | BCM_rand_bytes(seed, sizeof(seed)); |
1182 | 8 | CONSTTIME_SECRET(seed, sizeof(seed)); |
1183 | 8 | if (optional_out_seed) { |
1184 | 0 | OPENSSL_memcpy(optional_out_seed, seed, sizeof(seed)); |
1185 | 0 | } |
1186 | 8 | BCM_mlkem1024_generate_key_external_seed(out_encoded_public_key, |
1187 | 8 | out_private_key, seed); |
1188 | 8 | return bcm_infallible::not_approved; |
1189 | 8 | } |
1190 | | |
1191 | | bcm_status BCM_mlkem1024_private_key_from_seed( |
1192 | | struct BCM_mlkem1024_private_key *out_private_key, const uint8_t *seed, |
1193 | 0 | size_t seed_len) { |
1194 | 0 | if (seed_len != BCM_MLKEM_SEED_BYTES) { |
1195 | 0 | return bcm_status::failure; |
1196 | 0 | } |
1197 | 0 | uint8_t public_key_bytes[BCM_MLKEM1024_PUBLIC_KEY_BYTES]; |
1198 | 0 | BCM_mlkem1024_generate_key_external_seed(public_key_bytes, out_private_key, |
1199 | 0 | seed); |
1200 | 0 | return bcm_status::not_approved; |
1201 | 0 | } |
1202 | | |
1203 | | bcm_infallible BCM_mlkem768_generate_key_external_seed( |
1204 | | uint8_t out_encoded_public_key[BCM_MLKEM768_PUBLIC_KEY_BYTES], |
1205 | | struct BCM_mlkem768_private_key *out_private_key, |
1206 | 67.2k | const uint8_t seed[BCM_MLKEM_SEED_BYTES]) { |
1207 | 67.2k | mlkem::private_key<RANK768> *priv = |
1208 | 67.2k | mlkem::private_key_768_from_external(out_private_key); |
1209 | 67.2k | mlkem_generate_key_external_seed(out_encoded_public_key, priv, seed); |
1210 | 67.2k | return bcm_infallible::approved; |
1211 | 67.2k | } |
1212 | | |
1213 | | bcm_infallible BCM_mlkem1024_generate_key_external_seed( |
1214 | | uint8_t out_encoded_public_key[BCM_MLKEM1024_PUBLIC_KEY_BYTES], |
1215 | | struct BCM_mlkem1024_private_key *out_private_key, |
1216 | 8 | const uint8_t seed[BCM_MLKEM_SEED_BYTES]) { |
1217 | 8 | mlkem::private_key<RANK1024> *priv = |
1218 | 8 | mlkem::private_key_1024_from_external(out_private_key); |
1219 | 8 | mlkem_generate_key_external_seed(out_encoded_public_key, priv, seed); |
1220 | 8 | return bcm_infallible::approved; |
1221 | 8 | } |
1222 | | |
1223 | | bcm_infallible BCM_mlkem768_public_from_private( |
1224 | | struct BCM_mlkem768_public_key *out_public_key, |
1225 | 0 | const struct BCM_mlkem768_private_key *private_key) { |
1226 | 0 | struct mlkem::public_key<RANK768> *const pub = |
1227 | 0 | mlkem::public_key_768_from_external(out_public_key); |
1228 | 0 | const struct mlkem::private_key<RANK768> *const priv = |
1229 | 0 | mlkem::private_key_768_from_external(private_key); |
1230 | 0 | *pub = priv->pub; |
1231 | 0 | return bcm_infallible::approved; |
1232 | 0 | } |
1233 | | |
1234 | | bcm_infallible BCM_mlkem1024_public_from_private( |
1235 | | struct BCM_mlkem1024_public_key *out_public_key, |
1236 | 0 | const struct BCM_mlkem1024_private_key *private_key) { |
1237 | 0 | struct mlkem::public_key<RANK1024> *const pub = |
1238 | 0 | mlkem::public_key_1024_from_external(out_public_key); |
1239 | 0 | const struct mlkem::private_key<RANK1024> *const priv = |
1240 | 0 | mlkem::private_key_1024_from_external(private_key); |
1241 | 0 | *pub = priv->pub; |
1242 | 0 | return bcm_infallible::approved; |
1243 | 0 | } |
1244 | | |
1245 | | // Calls |MLKEM768_encap_external_entropy| with random bytes from |
1246 | | // |BCM_rand_bytes| |
1247 | | bcm_infallible BCM_mlkem768_encap( |
1248 | | uint8_t out_ciphertext[BCM_MLKEM768_CIPHERTEXT_BYTES], |
1249 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
1250 | 205 | const struct BCM_mlkem768_public_key *public_key) { |
1251 | 205 | uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]; |
1252 | 205 | BCM_rand_bytes(entropy, BCM_MLKEM_ENCAP_ENTROPY); |
1253 | 205 | CONSTTIME_SECRET(entropy, BCM_MLKEM_ENCAP_ENTROPY); |
1254 | 205 | BCM_mlkem768_encap_external_entropy(out_ciphertext, out_shared_secret, |
1255 | 205 | public_key, entropy); |
1256 | 205 | return bcm_infallible::approved; |
1257 | 205 | } |
1258 | | |
1259 | | bcm_infallible BCM_mlkem1024_encap( |
1260 | | uint8_t out_ciphertext[BCM_MLKEM1024_CIPHERTEXT_BYTES], |
1261 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
1262 | 0 | const struct BCM_mlkem1024_public_key *public_key) { |
1263 | 0 | uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]; |
1264 | 0 | BCM_rand_bytes(entropy, BCM_MLKEM_ENCAP_ENTROPY); |
1265 | 0 | CONSTTIME_SECRET(entropy, BCM_MLKEM_ENCAP_ENTROPY); |
1266 | 0 | BCM_mlkem1024_encap_external_entropy(out_ciphertext, out_shared_secret, |
1267 | 0 | public_key, entropy); |
1268 | 0 | return bcm_infallible::approved; |
1269 | 0 | } |
1270 | | |
1271 | | bcm_infallible BCM_mlkem768_encap_external_entropy( |
1272 | | uint8_t out_ciphertext[BCM_MLKEM768_CIPHERTEXT_BYTES], |
1273 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
1274 | | const struct BCM_mlkem768_public_key *public_key, |
1275 | 205 | const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]) { |
1276 | 205 | const struct mlkem::public_key<RANK768> *pub = |
1277 | 205 | mlkem::public_key_768_from_external(public_key); |
1278 | 205 | mlkem_encap_external_entropy(out_ciphertext, out_shared_secret, pub, entropy); |
1279 | 205 | return bcm_infallible::approved; |
1280 | 205 | } |
1281 | | |
1282 | | bcm_infallible BCM_mlkem1024_encap_external_entropy( |
1283 | | uint8_t out_ciphertext[BCM_MLKEM1024_CIPHERTEXT_BYTES], |
1284 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
1285 | | const struct BCM_mlkem1024_public_key *public_key, |
1286 | 0 | const uint8_t entropy[BCM_MLKEM_ENCAP_ENTROPY]) { |
1287 | 0 | const struct mlkem::public_key<RANK1024> *pub = |
1288 | 0 | mlkem::public_key_1024_from_external(public_key); |
1289 | 0 | mlkem_encap_external_entropy(out_ciphertext, out_shared_secret, pub, entropy); |
1290 | 0 | return bcm_infallible::approved; |
1291 | 0 | } |
1292 | | |
1293 | | bcm_status BCM_mlkem768_decap( |
1294 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
1295 | | const uint8_t *ciphertext, size_t ciphertext_len, |
1296 | 21 | const struct BCM_mlkem768_private_key *private_key) { |
1297 | 21 | if (ciphertext_len != BCM_MLKEM768_CIPHERTEXT_BYTES) { |
1298 | 0 | BCM_rand_bytes(out_shared_secret, BCM_MLKEM_SHARED_SECRET_BYTES); |
1299 | 0 | return bcm_status::failure; |
1300 | 0 | } |
1301 | 21 | const struct mlkem::private_key<RANK768> *priv = |
1302 | 21 | mlkem::private_key_768_from_external(private_key); |
1303 | 21 | mlkem_decap(out_shared_secret, ciphertext, priv); |
1304 | 21 | return bcm_status::approved; |
1305 | 21 | } |
1306 | | |
1307 | | bcm_status BCM_mlkem1024_decap( |
1308 | | uint8_t out_shared_secret[BCM_MLKEM_SHARED_SECRET_BYTES], |
1309 | | const uint8_t *ciphertext, size_t ciphertext_len, |
1310 | 0 | const struct BCM_mlkem1024_private_key *private_key) { |
1311 | 0 | if (ciphertext_len != BCM_MLKEM1024_CIPHERTEXT_BYTES) { |
1312 | 0 | BCM_rand_bytes(out_shared_secret, BCM_MLKEM_SHARED_SECRET_BYTES); |
1313 | 0 | return bcm_status::failure; |
1314 | 0 | } |
1315 | 0 | const struct mlkem::private_key<RANK1024> *priv = |
1316 | 0 | mlkem::private_key_1024_from_external(private_key); |
1317 | 0 | mlkem_decap(out_shared_secret, ciphertext, priv); |
1318 | 0 | return bcm_status::approved; |
1319 | 0 | } |
1320 | | |
1321 | | bcm_status BCM_mlkem768_marshal_public_key( |
1322 | 0 | CBB *out, const struct BCM_mlkem768_public_key *public_key) { |
1323 | 0 | return mlkem_marshal_public_key( |
1324 | 0 | out, mlkem::public_key_768_from_external(public_key)); |
1325 | 0 | } |
1326 | | |
1327 | | bcm_status BCM_mlkem1024_marshal_public_key( |
1328 | 0 | CBB *out, const struct BCM_mlkem1024_public_key *public_key) { |
1329 | 0 | return mlkem_marshal_public_key( |
1330 | 0 | out, mlkem::public_key_1024_from_external(public_key)); |
1331 | 0 | } |
1332 | | |
1333 | | bcm_status BCM_mlkem768_parse_public_key( |
1334 | 281 | struct BCM_mlkem768_public_key *public_key, CBS *in) { |
1335 | 281 | struct mlkem::public_key<RANK768> *pub = |
1336 | 281 | mlkem::public_key_768_from_external(public_key); |
1337 | 281 | if (!mlkem_parse_public_key(pub, in)) { |
1338 | 44 | return bcm_status::failure; |
1339 | 44 | } |
1340 | 237 | return bcm_status::approved; |
1341 | 281 | } |
1342 | | |
1343 | | bcm_status BCM_mlkem1024_parse_public_key( |
1344 | 0 | struct BCM_mlkem1024_public_key *public_key, CBS *in) { |
1345 | 0 | struct mlkem::public_key<RANK1024> *pub = |
1346 | 0 | mlkem::public_key_1024_from_external(public_key); |
1347 | 0 | if (!mlkem_parse_public_key(pub, in)) { |
1348 | 0 | return bcm_status::failure; |
1349 | 0 | } |
1350 | 0 | return bcm_status::approved; |
1351 | 0 | } |
1352 | | |
1353 | | bcm_status BCM_mlkem768_marshal_private_key( |
1354 | 0 | CBB *out, const struct BCM_mlkem768_private_key *private_key) { |
1355 | 0 | const struct mlkem::private_key<RANK768> *const priv = |
1356 | 0 | mlkem::private_key_768_from_external(private_key); |
1357 | 0 | if (!mlkem_marshal_private_key(out, priv)) { |
1358 | 0 | return bcm_status::failure; |
1359 | 0 | } |
1360 | 0 | return bcm_status::approved; |
1361 | 0 | } |
1362 | | |
1363 | | bcm_status BCM_mlkem1024_marshal_private_key( |
1364 | 0 | CBB *out, const struct BCM_mlkem1024_private_key *private_key) { |
1365 | 0 | const struct mlkem::private_key<RANK1024> *const priv = |
1366 | 0 | mlkem::private_key_1024_from_external(private_key); |
1367 | 0 | if (!mlkem_marshal_private_key(out, priv)) { |
1368 | 0 | return bcm_status::failure; |
1369 | 0 | } |
1370 | 0 | return bcm_status::approved; |
1371 | 0 | } |
1372 | | |
1373 | | bcm_status BCM_mlkem768_parse_private_key( |
1374 | 0 | struct BCM_mlkem768_private_key *out_private_key, CBS *in) { |
1375 | 0 | struct mlkem::private_key<RANK768> *const priv = |
1376 | 0 | mlkem::private_key_768_from_external(out_private_key); |
1377 | 0 | if (!mlkem_parse_private_key(priv, in)) { |
1378 | 0 | return bcm_status::failure; |
1379 | 0 | } |
1380 | 0 | return bcm_status::approved; |
1381 | 0 | } |
1382 | | |
1383 | | bcm_status BCM_mlkem1024_parse_private_key( |
1384 | 0 | struct BCM_mlkem1024_private_key *out_private_key, CBS *in) { |
1385 | 0 | struct mlkem::private_key<RANK1024> *const priv = |
1386 | 0 | mlkem::private_key_1024_from_external(out_private_key); |
1387 | 0 | if (!mlkem_parse_private_key(priv, in)) { |
1388 | 0 | return bcm_status::failure; |
1389 | 0 | } |
1390 | 0 | return bcm_status::approved; |
1391 | 0 | } |
1392 | | |
1393 | 0 | int boringssl_self_test_mlkem() { |
1394 | 0 | return mlkem::fips::keygen_self_test() && mlkem::fips::encap_self_test() && |
1395 | 0 | mlkem::fips::decap_self_test(); |
1396 | 0 | } |