Coverage Report

Created: 2026-03-19 06:22

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