Coverage Report

Created: 2025-08-28 06:59

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