Botan 3.4.0
Crypto and TLS for C&
kyber.cpp
Go to the documentation of this file.
1/*
2 * Crystals Kyber key encapsulation mechanism
3 * Based on the public domain reference implementation by the
4 * designers (https://github.com/pq-crystals/kyber)
5 *
6 * Further changes
7 * (C) 2021-2022 Jack Lloyd
8 * (C) 2021-2022 Manuel Glaser and Michael Boric, Rohde & Schwarz Cybersecurity
9 * (C) 2021-2022 René Meusel and Hannes Rantzsch, neXenio GmbH
10 *
11 * Botan is released under the Simplified BSD License (see license.txt)
12 */
13
14#include <botan/kyber.h>
15
16#include <botan/assert.h>
17#include <botan/ber_dec.h>
18#include <botan/hash.h>
19#include <botan/mem_ops.h>
20#include <botan/pubkey.h>
21#include <botan/rng.h>
22#include <botan/secmem.h>
23
24#include <botan/internal/ct_utils.h>
25#include <botan/internal/fmt.h>
26#include <botan/internal/kyber_symmetric_primitives.h>
27#include <botan/internal/loadstor.h>
28#include <botan/internal/pk_ops_impl.h>
29#include <botan/internal/stl_util.h>
30
31#if defined(BOTAN_HAS_KYBER)
32 #include <botan/internal/kyber_modern.h>
33#endif
34
35#if defined(BOTAN_HAS_KYBER_90S)
36 #include <botan/internal/kyber_90s.h>
37#endif
38
39#include <algorithm>
40#include <array>
41#include <iterator>
42#include <limits>
43#include <memory>
44#include <optional>
45#include <vector>
46
47namespace Botan {
48
49namespace {
50
51KyberMode::Mode kyber_mode_from_string(std::string_view str) {
52 if(str == "Kyber-512-90s-r3") {
54 }
55 if(str == "Kyber-768-90s-r3") {
57 }
58 if(str == "Kyber-1024-90s-r3") {
60 }
61 if(str == "Kyber-512-r3") {
63 }
64 if(str == "Kyber-768-r3") {
66 }
67 if(str == "Kyber-1024-r3") {
69 }
70
71 throw Invalid_Argument(fmt("'{}' is not a valid Kyber mode name", str));
72}
73
74/**
75 * Constant time implementation for computing an unsigned integer division
76 * with KyberConstants::Q = 3329.
77 *
78 * It enforces the optimization of various compilers,
79 * replacing the division operation with multiplication and shifts.
80 *
81 * This implementation is only valid for integers <= 2**20
82 *
83 * @returns (a / KyberConstants::Q)
84 */
85uint16_t ct_int_div_kyber_q(uint32_t a) {
86 BOTAN_DEBUG_ASSERT(a < (1 << 18));
87
88 /*
89 Constants based on "Hacker's Delight" (Second Edition) by Henry
90 S. Warren, Jr. Chapter 10-9 "Unsigned Division by Divisors >= 1"
91 */
92 const uint64_t m = 161271;
93 const size_t p = 29;
94 return static_cast<uint16_t>((a * m) >> p);
95}
96
97} // namespace
98
99KyberMode::KyberMode(Mode mode) : m_mode(mode) {}
100
101KyberMode::KyberMode(const OID& oid) : m_mode(kyber_mode_from_string(oid.to_formatted_string())) {}
102
103KyberMode::KyberMode(std::string_view str) : m_mode(kyber_mode_from_string(str)) {}
104
108
109std::string KyberMode::to_string() const {
110 switch(m_mode) {
111 case Kyber512_90s:
112 return "Kyber-512-90s-r3";
113 case Kyber768_90s:
114 return "Kyber-768-90s-r3";
115 case Kyber1024_90s:
116 return "Kyber-1024-90s-r3";
117 case Kyber512_R3:
118 return "Kyber-512-r3";
119 case Kyber768_R3:
120 return "Kyber-768-r3";
121 case Kyber1024_R3:
122 return "Kyber-1024-r3";
123 }
124
126}
127
128bool KyberMode::is_90s() const {
129 return m_mode == Kyber512_90s || m_mode == Kyber768_90s || m_mode == Kyber1024_90s;
130}
131
133 return !is_90s();
134}
135
137#if defined(BOTAN_HAS_KYBER)
138 if(is_modern()) {
139 return true;
140 }
141#endif
142
143#if defined(BOTAN_HAS_KYBER_90S)
144 if(is_90s()) {
145 return true;
146 }
147#endif
148
149 return false;
150}
151
152namespace {
153
154class KyberConstants {
155 public:
156 static constexpr size_t N = 256;
157 static constexpr size_t Q = 3329;
158 static constexpr size_t Q_Inv = 62209;
159
160 static constexpr int16_t zetas[128] = {
161 2285, 2571, 2970, 1812, 1493, 1422, 287, 202, 3158, 622, 1577, 182, 962, 2127, 1855, 1468,
162 573, 2004, 264, 383, 2500, 1458, 1727, 3199, 2648, 1017, 732, 608, 1787, 411, 3124, 1758,
163 1223, 652, 2777, 1015, 2036, 1491, 3047, 1785, 516, 3321, 3009, 2663, 1711, 2167, 126, 1469,
164 2476, 3239, 3058, 830, 107, 1908, 3082, 2378, 2931, 961, 1821, 2604, 448, 2264, 677, 2054,
165 2226, 430, 555, 843, 2078, 871, 1550, 105, 422, 587, 177, 3094, 3038, 2869, 1574, 1653,
166 3083, 778, 1159, 3182, 2552, 1483, 2727, 1119, 1739, 644, 2457, 349, 418, 329, 3173, 3254,
167 817, 1097, 603, 610, 1322, 2044, 1864, 384, 2114, 3193, 1218, 1994, 2455, 220, 2142, 1670,
168 2144, 1799, 2051, 794, 1819, 2475, 2459, 478, 3221, 3021, 996, 991, 958, 1869, 1522, 1628};
169
170 static constexpr int16_t zetas_inv[128] = {
171 1701, 1807, 1460, 2371, 2338, 2333, 308, 108, 2851, 870, 854, 1510, 2535, 1278, 1530, 1185,
172 1659, 1187, 3109, 874, 1335, 2111, 136, 1215, 2945, 1465, 1285, 2007, 2719, 2726, 2232, 2512,
173 75, 156, 3000, 2911, 2980, 872, 2685, 1590, 2210, 602, 1846, 777, 147, 2170, 2551, 246,
174 1676, 1755, 460, 291, 235, 3152, 2742, 2907, 3224, 1779, 2458, 1251, 2486, 2774, 2899, 1103,
175 1275, 2652, 1065, 2881, 725, 1508, 2368, 398, 951, 247, 1421, 3222, 2499, 271, 90, 853,
176 1860, 3203, 1162, 1618, 666, 320, 8, 2813, 1544, 282, 1838, 1293, 2314, 552, 2677, 2106,
177 1571, 205, 2918, 1542, 2721, 2597, 2312, 681, 130, 1602, 1871, 829, 2946, 3065, 1325, 2756,
178 1861, 1474, 1202, 2367, 3147, 1752, 2707, 171, 3127, 3042, 1907, 1836, 1517, 359, 758, 1441};
179
180 static constexpr size_t kSymBytes = 32;
181 static constexpr size_t kSeedLength = kSymBytes;
182 static constexpr size_t kSerializedPolynomialByteLength = N / 2 * 3;
183 static constexpr size_t kPublicKeyHashLength = 32;
184 static constexpr size_t kZLength = kSymBytes;
185
186 public:
187 KyberConstants(const KyberMode mode) : m_mode(mode) {
188 switch(mode.mode()) {
191 m_nist_strength = 128;
192 m_k = 2;
193 m_eta1 = 3;
194 break;
195
198 m_nist_strength = 192;
199 m_k = 3;
200 m_eta1 = 2;
201 break;
202
205 m_nist_strength = 256;
206 m_k = 4;
207 m_eta1 = 2;
208 break;
209
210 default:
212 }
213
214#ifdef BOTAN_HAS_KYBER_90S
215 if(mode.is_90s()) {
216 m_symmetric_primitives = std::make_unique<Kyber_90s_Symmetric_Primitives>();
217 }
218#endif
219#ifdef BOTAN_HAS_KYBER
220 if(!mode.is_90s()) {
221 m_symmetric_primitives = std::make_unique<Kyber_Modern_Symmetric_Primitives>();
222 }
223#endif
224
225 if(!m_symmetric_primitives) {
226 throw Not_Implemented("requested Kyber mode is not enabled in this build");
227 }
228 }
229
230 ~KyberConstants() = default;
231
232 KyberConstants(const KyberConstants& other) : KyberConstants(other.m_mode) {}
233
234 KyberConstants(KyberConstants&& other) = default;
235 KyberConstants& operator=(const KyberConstants& other) = delete;
236 KyberConstants& operator=(KyberConstants&& other) = default;
237
238 KyberMode mode() const { return m_mode; }
239
240 size_t estimated_strength() const { return m_nist_strength; }
241
242 uint8_t k() const { return m_k; }
243
244 uint8_t eta1() const { return m_eta1; }
245
246 uint8_t eta2() const { return 2; }
247
248 size_t polynomial_vector_byte_length() const { return kSerializedPolynomialByteLength * k(); }
249
250 size_t public_key_byte_length() const { return polynomial_vector_byte_length() + kSeedLength; }
251
252 size_t private_key_byte_length() const {
253 return polynomial_vector_byte_length() + public_key_byte_length() + kPublicKeyHashLength + kZLength;
254 }
255
256 std::unique_ptr<HashFunction> G() const { return m_symmetric_primitives->G(); }
257
258 std::unique_ptr<HashFunction> H() const { return m_symmetric_primitives->H(); }
259
260 std::unique_ptr<HashFunction> KDF() const { return m_symmetric_primitives->KDF(); }
261
262 Botan::XOF& XOF(std::span<const uint8_t> seed, std::tuple<uint8_t, uint8_t> matrix_position) const {
263 return m_symmetric_primitives->XOF(seed, matrix_position);
264 }
265
266 secure_vector<uint8_t> PRF(std::span<const uint8_t> seed, const uint8_t nonce, const size_t outlen) const {
267 return m_symmetric_primitives->PRF(seed, nonce, outlen);
268 }
269
270 private:
271 KyberMode m_mode;
272 std::unique_ptr<Kyber_Symmetric_Primitives> m_symmetric_primitives;
273 size_t m_nist_strength;
274 uint8_t m_k;
275 uint8_t m_eta1;
276};
277
278class Polynomial {
279 public:
280 /**
281 * Applies conditional subtraction of q to each coefficient of the polynomial.
282 */
283 void csubq() {
284 for(auto& coeff : m_coeffs) {
285 coeff -= KyberConstants::Q;
286 coeff += (coeff >> 15) & KyberConstants::Q;
287 }
288 }
289
290 /**
291 * Applies Barrett reduction to all coefficients of the polynomial
292 */
293 void reduce() {
294 for(auto& c : m_coeffs) {
295 c = barrett_reduce(c);
296 }
297 }
298
299 template <typename T = std::vector<uint8_t>>
300 T to_bytes() {
301 this->csubq();
302
303 T r(KyberConstants::kSerializedPolynomialByteLength);
304
305 for(size_t i = 0; i < size() / 2; ++i) {
306 const uint16_t t0 = m_coeffs[2 * i];
307 const uint16_t t1 = m_coeffs[2 * i + 1];
308 r[3 * i + 0] = static_cast<uint8_t>(t0 >> 0);
309 r[3 * i + 1] = static_cast<uint8_t>((t0 >> 8) | (t1 << 4));
310 r[3 * i + 2] = static_cast<uint8_t>(t1 >> 4);
311 }
312
313 return r;
314 }
315
316 /**
317 * Given an array of uniformly random bytes, compute polynomial with coefficients
318 * distributed according to a centered binomial distribution with parameter eta=2
319 */
320 static Polynomial cbd2(std::span<const uint8_t> buf) {
321 Polynomial r;
322
323 BOTAN_ASSERT(buf.size() == (2 * r.size() / 4), "wrong input buffer size for cbd2");
324
325 for(size_t i = 0; i < r.size() / 8; ++i) {
326 uint32_t t = load_le<uint32_t>(buf.data(), i);
327 uint32_t d = t & 0x55555555;
328 d += (t >> 1) & 0x55555555;
329
330 for(size_t j = 0; j < 8; ++j) {
331 int16_t a = (d >> (4 * j + 0)) & 0x3;
332 int16_t b = (d >> (4 * j + 2)) & 0x3;
333 r.m_coeffs[8 * i + j] = a - b;
334 }
335 }
336
337 return r;
338 }
339
340 /**
341 * Given an array of uniformly random bytes, compute polynomial with coefficients
342 * distributed according to a centered binomial distribution with parameter eta=3
343 *
344 * This function is only needed for Kyber-512
345 */
346 static Polynomial cbd3(std::span<const uint8_t> buf) {
347 Polynomial r;
348
349 BOTAN_ASSERT(buf.size() == (3 * r.size() / 4), "wrong input buffer size for cbd3");
350
351 // Note: load_le<> does not support loading a 3-byte value
352 const auto load_le24 = [](const uint8_t in[], const size_t off) {
353 const auto off3 = off * 3;
354 return make_uint32(0, in[off3 + 2], in[off3 + 1], in[off3]);
355 };
356
357 for(size_t i = 0; i < r.size() / 4; ++i) {
358 uint32_t t = load_le24(buf.data(), i);
359 uint32_t d = t & 0x00249249;
360 d += (t >> 1) & 0x00249249;
361 d += (t >> 2) & 0x00249249;
362
363 for(size_t j = 0; j < 4; ++j) {
364 int16_t a = (d >> (6 * j + 0)) & 0x7;
365 int16_t b = (d >> (6 * j + 3)) & 0x7;
366 r.m_coeffs[4 * i + j] = a - b;
367 }
368 }
369 return r;
370 }
371
372 /**
373 * Sample a polynomial deterministically from a seed and a nonce, with output
374 * polynomial close to centered binomial distribution with parameter eta=2.
375 */
376 static Polynomial getnoise_eta2(std::span<const uint8_t> seed, uint8_t nonce, const KyberConstants& mode) {
377 const auto eta2 = mode.eta2();
378 BOTAN_ASSERT(eta2 == 2, "Invalid eta2 value");
379
380 const auto outlen = eta2 * KyberConstants::N / 4;
381 return Polynomial::cbd2(mode.PRF(seed, nonce, outlen));
382 }
383
384 /**
385 * Sample a polynomial deterministically from a seed and a nonce, with output
386 * polynomial close to centered binomial distribution with parameter mode.eta1()
387 */
388 static Polynomial getnoise_eta1(std::span<const uint8_t> seed, uint8_t nonce, const KyberConstants& mode) {
389 const auto eta1 = mode.eta1();
390 BOTAN_ASSERT(eta1 == 2 || eta1 == 3, "Invalid eta1 value");
391
392 const auto outlen = eta1 * KyberConstants::N / 4;
393 return (eta1 == 2) ? Polynomial::cbd2(mode.PRF(seed, nonce, outlen))
394 : Polynomial::cbd3(mode.PRF(seed, nonce, outlen));
395 }
396
397 static Polynomial from_bytes(std::span<const uint8_t> a) {
398 Polynomial r;
399 for(size_t i = 0; i < r.size() / 2; ++i) {
400 r.m_coeffs[2 * i] = ((a[3 * i + 0] >> 0) | (static_cast<uint16_t>(a[3 * i + 1]) << 8)) & 0xFFF;
401 r.m_coeffs[2 * i + 1] = ((a[3 * i + 1] >> 4) | (static_cast<uint16_t>(a[3 * i + 2]) << 4)) & 0xFFF;
402 }
403 return r;
404 }
405
406 static Polynomial from_message(std::span<const uint8_t> msg) {
407 BOTAN_ASSERT(msg.size() == KyberConstants::N / 8, "message length must be Kyber_N/8 bytes");
408
409 Polynomial r;
410 for(size_t i = 0; i < r.size() / 8; ++i) {
411 for(size_t j = 0; j < 8; ++j) {
412 const auto mask = -static_cast<int16_t>((msg[i] >> j) & 1);
413 r.m_coeffs[8 * i + j] = mask & ((KyberConstants::Q + 1) / 2);
414 }
415 }
416 return r;
417 }
418
419 template <typename T = secure_vector<uint8_t>>
420 T to_message() {
421 T result(size() / 8);
422
423 this->csubq();
424
425 for(size_t i = 0; i < size() / 8; ++i) {
426 result[i] = 0;
427 for(size_t j = 0; j < 8; ++j) {
428 const uint16_t t =
429 ct_int_div_kyber_q((static_cast<uint16_t>(this->m_coeffs[8 * i + j]) << 1) + KyberConstants::Q / 2);
430 result[i] |= (t & 1) << j;
431 }
432 }
433
434 return result;
435 }
436
437 /**
438 * Adds two polynomials element-wise. Does not perform a reduction after the addition.
439 * Therefore this operation might cause an integer overflow.
440 */
441 Polynomial& operator+=(const Polynomial& other) {
442 for(size_t i = 0; i < this->size(); ++i) {
443 BOTAN_DEBUG_ASSERT(static_cast<int32_t>(this->m_coeffs[i]) + other.m_coeffs[i] <=
444 std::numeric_limits<int16_t>::max());
445 this->m_coeffs[i] = this->m_coeffs[i] + other.m_coeffs[i];
446 }
447 return *this;
448 }
449
450 /**
451 * Subtracts two polynomials element-wise. Does not perform a reduction after the subtraction.
452 * Therefore this operation might cause an integer underflow.
453 */
454 Polynomial& operator-=(const Polynomial& other) {
455 for(size_t i = 0; i < this->size(); ++i) {
456 BOTAN_DEBUG_ASSERT(static_cast<int32_t>(other.m_coeffs[i]) - this->m_coeffs[i] >=
457 std::numeric_limits<int16_t>::min());
458 this->m_coeffs[i] = other.m_coeffs[i] - this->m_coeffs[i];
459 }
460 return *this;
461 }
462
463 /**
464 * Multiplication of two polynomials in NTT domain
465 */
466 static Polynomial basemul_montgomery(const Polynomial& a, const Polynomial& b) {
467 /**
468 * Multiplication of polynomials in Zq[X]/(X^2-zeta) used for
469 * multiplication of elements in Rq in NTT domain.
470 */
471 auto basemul = [](int16_t r[2], const int16_t s[2], const int16_t t[2], const int16_t zeta) {
472 r[0] = fqmul(s[1], t[1]);
473 r[0] = fqmul(r[0], zeta);
474 r[0] += fqmul(s[0], t[0]);
475
476 r[1] = fqmul(s[0], t[1]);
477 r[1] += fqmul(s[1], t[0]);
478 };
479
480 Polynomial r;
481
482 for(size_t i = 0; i < r.size() / 4; ++i) {
483 basemul(&r.m_coeffs[4 * i], &a.m_coeffs[4 * i], &b.m_coeffs[4 * i], KyberConstants::zetas[64 + i]);
484 basemul(
485 &r.m_coeffs[4 * i + 2], &a.m_coeffs[4 * i + 2], &b.m_coeffs[4 * i + 2], -KyberConstants::zetas[64 + i]);
486 }
487
488 return r;
489 }
490
491 /**
492 * Run rejection sampling on uniform random bytes to generate uniform
493 * random integers mod q.
494 */
495 static Polynomial sample_rej_uniform(XOF& xof) {
496 Polynomial p;
497
498 size_t count = 0;
499 while(count < p.size()) {
500 std::array<uint8_t, 3> buf;
501 xof.output(buf);
502
503 const uint16_t val0 = ((buf[0] >> 0) | (static_cast<uint16_t>(buf[1]) << 8)) & 0xFFF;
504 const uint16_t val1 = ((buf[1] >> 4) | (static_cast<uint16_t>(buf[2]) << 4)) & 0xFFF;
505
506 if(val0 < KyberConstants::Q) {
507 p.m_coeffs[count++] = val0;
508 }
509 if(count < p.size() && val1 < KyberConstants::Q) {
510 p.m_coeffs[count++] = val1;
511 }
512 }
513
514 return p;
515 }
516
517 /**
518 * Inplace conversion of all coefficients of a polynomial from normal
519 * domain to Montgomery domain.
520 */
521 void tomont() {
522 constexpr int16_t f = (1ULL << 32) % KyberConstants::Q;
523 for(auto& c : m_coeffs) {
524 c = montgomery_reduce(static_cast<int32_t>(c) * f);
525 }
526 }
527
528 /**
529 * Computes negacyclic number-theoretic transform (NTT) of a polynomial in place;
530 * inputs assumed to be in normal order, output in bitreversed order.
531 */
532 void ntt() {
533 for(size_t len = size() / 2, k = 0; len >= 2; len /= 2) {
534 for(size_t start = 0, j = 0; start < size(); start = j + len) {
535 const auto zeta = KyberConstants::zetas[++k];
536 for(j = start; j < start + len; ++j) {
537 const auto t = fqmul(zeta, m_coeffs[j + len]);
538 m_coeffs[j + len] = m_coeffs[j] - t;
539 m_coeffs[j] = m_coeffs[j] + t;
540 }
541 }
542 }
543
544 reduce();
545 }
546
547 /**
548 * Computes inverse of negacyclic number-theoretic transform (NTT) of a polynomial
549 * in place; inputs assumed to be in bitreversed order, output in normal order.
550 */
551 void invntt_tomont() {
552 for(size_t len = 2, k = 0; len <= size() / 2; len *= 2) {
553 for(size_t start = 0, j = 0; start < size(); start = j + len) {
554 const auto zeta = KyberConstants::zetas_inv[k++];
555 for(j = start; j < start + len; ++j) {
556 const auto t = m_coeffs[j];
557 m_coeffs[j] = barrett_reduce(t + m_coeffs[j + len]);
558 m_coeffs[j + len] = fqmul(zeta, t - m_coeffs[j + len]);
559 }
560 }
561 }
562
563 for(auto& c : m_coeffs) {
564 c = fqmul(c, KyberConstants::zetas_inv[127]);
565 }
566 }
567
568 size_t size() const { return m_coeffs.size(); }
569
570 int16_t operator[](size_t idx) const { return m_coeffs[idx]; }
571
572 int16_t& operator[](size_t idx) { return m_coeffs[idx]; }
573
574 private:
575 /**
576 * Barrett reduction; given a 16-bit integer a, computes 16-bit integer congruent
577 * to a mod q in {0,...,q}.
578 */
579 static int16_t barrett_reduce(int16_t a) {
580 int16_t t;
581 constexpr int16_t v = ((1U << 26) + KyberConstants::Q / 2) / KyberConstants::Q;
582
583 t = static_cast<int32_t>(v) * a >> 26;
584 t *= KyberConstants::Q;
585 return a - t;
586 }
587
588 /**
589 * Multiplication followed by Montgomery reduction.
590 */
591 static int16_t fqmul(int16_t a, int16_t b) { return montgomery_reduce(static_cast<int32_t>(a) * b); }
592
593 /**
594 * Montgomery reduction; given a 32-bit integer a, computes 16-bit integer
595 * congruent to a * R^-1 mod q, where R=2^16
596 */
597 static int16_t montgomery_reduce(int32_t a) {
598 const int16_t u = static_cast<int16_t>(a * KyberConstants::Q_Inv);
599 int32_t t = static_cast<int32_t>(u) * KyberConstants::Q;
600 t = a - t;
601 t >>= 16;
602 return static_cast<int16_t>(t);
603 }
604
605 std::array<int16_t, KyberConstants::N> m_coeffs;
606};
607
608class PolynomialVector {
609 public:
610 PolynomialVector() = delete;
611
612 explicit PolynomialVector(const size_t k) : m_vec(k) {}
613
614 static PolynomialVector from_bytes(std::span<const uint8_t> a, const KyberConstants& mode) {
615 BOTAN_ASSERT(a.size() == mode.polynomial_vector_byte_length(), "wrong byte length for frombytes");
616
617 PolynomialVector r(mode.k());
618 for(size_t i = 0; i < mode.k(); ++i) {
619 r.m_vec[i] = Polynomial::from_bytes(a.subspan(0, KyberConstants::kSerializedPolynomialByteLength));
620 a = a.subspan(KyberConstants::kSerializedPolynomialByteLength);
621 }
622 return r;
623 }
624
625 /**
626 * Pointwise multiply elements of a and b, accumulate into r, and multiply by 2^-16.
627 */
628 static Polynomial pointwise_acc_montgomery(const PolynomialVector& a, const PolynomialVector& b) {
629 BOTAN_ASSERT(a.m_vec.size() == b.m_vec.size(),
630 "pointwise_acc_montgomery works on equally sized "
631 "PolynomialVectors only");
632
633 auto r = Polynomial::basemul_montgomery(a.m_vec[0], b.m_vec[0]);
634 for(size_t i = 1; i < a.m_vec.size(); ++i) {
635 r += Polynomial::basemul_montgomery(a.m_vec[i], b.m_vec[i]);
636 }
637
638 r.reduce();
639 return r;
640 }
641
642 static PolynomialVector getnoise_eta2(std::span<const uint8_t> seed, uint8_t nonce, const KyberConstants& mode) {
643 PolynomialVector r(mode.k());
644
645 for(auto& p : r.m_vec) {
646 p = Polynomial::getnoise_eta2(seed, nonce++, mode);
647 }
648
649 return r;
650 }
651
652 static PolynomialVector getnoise_eta1(std::span<const uint8_t> seed, uint8_t nonce, const KyberConstants& mode) {
653 PolynomialVector r(mode.k());
654
655 for(auto& p : r.m_vec) {
656 p = Polynomial::getnoise_eta1(seed, nonce++, mode);
657 }
658
659 return r;
660 }
661
662 template <typename T = std::vector<uint8_t>>
663 T to_bytes() {
664 T r;
665
666 r.reserve(m_vec.size() * KyberConstants::kSerializedPolynomialByteLength);
667 for(auto& v : m_vec) {
668 const auto poly = v.to_bytes<T>();
669 r.insert(r.end(), poly.begin(), poly.end());
670 }
671
672 return r;
673 }
674
675 /**
676 * Applies conditional subtraction of q to each coefficient of each element
677 * of the vector of polynomials.
678 */
679 void csubq() {
680 for(auto& p : m_vec) {
681 p.csubq();
682 }
683 }
684
685 PolynomialVector& operator+=(const PolynomialVector& other) {
686 BOTAN_ASSERT(m_vec.size() == other.m_vec.size(), "cannot add polynomial vectors of differing lengths");
687
688 for(size_t i = 0; i < m_vec.size(); ++i) {
689 m_vec[i] += other.m_vec[i];
690 }
691 return *this;
692 }
693
694 Polynomial& operator[](size_t idx) { return m_vec[idx]; }
695
696 /**
697 * Applies Barrett reduction to each coefficient of each element of a vector of polynomials.
698 */
699 void reduce() {
700 for(auto& v : m_vec) {
701 v.reduce();
702 }
703 }
704
705 /**
706 * Apply inverse NTT to all elements of a vector of polynomials and multiply by Montgomery factor 2^16.
707 */
708 void invntt_tomont() {
709 for(auto& v : m_vec) {
710 v.invntt_tomont();
711 }
712 }
713
714 /**
715 * Apply forward NTT to all elements of a vector of polynomials.
716 */
717 void ntt() {
718 for(auto& v : m_vec) {
719 v.ntt();
720 }
721 }
722
723 private:
724 std::vector<Polynomial> m_vec;
725};
726
727class PolynomialMatrix {
728 public:
729 PolynomialMatrix() = delete;
730
731 static PolynomialMatrix generate(std::span<const uint8_t> seed,
732 const bool transposed,
733 const KyberConstants& mode) {
734 BOTAN_ASSERT(seed.size() == KyberConstants::kSymBytes, "unexpected seed size");
735
736 PolynomialMatrix matrix(mode);
737
738 for(uint8_t i = 0; i < mode.k(); ++i) {
739 for(uint8_t j = 0; j < mode.k(); ++j) {
740 const auto pos = (transposed) ? std::tuple(i, j) : std::tuple(j, i);
741 matrix.m_mat[i][j] = Polynomial::sample_rej_uniform(mode.XOF(seed, pos));
742 }
743 }
744
745 return matrix;
746 }
747
748 PolynomialVector pointwise_acc_montgomery(const PolynomialVector& vec, const bool with_mont = false) const {
749 PolynomialVector result(m_mat.size());
750
751 for(size_t i = 0; i < m_mat.size(); ++i) {
752 result[i] = PolynomialVector::pointwise_acc_montgomery(m_mat[i], vec);
753 if(with_mont) {
754 result[i].tomont();
755 }
756 }
757
758 return result;
759 }
760
761 private:
762 explicit PolynomialMatrix(const KyberConstants& mode) : m_mat(mode.k(), PolynomialVector(mode.k())) {}
763
764 private:
765 std::vector<PolynomialVector> m_mat;
766};
767
768class Ciphertext {
769 public:
770 Ciphertext() = delete;
771
772 Ciphertext(PolynomialVector b, const Polynomial& v, KyberConstants mode) :
773 m_mode(std::move(mode)), m_b(std::move(b)), m_v(v) {}
774
775 static Ciphertext from_bytes(std::span<const uint8_t> buffer, const KyberConstants& mode) {
776 const size_t pvb = polynomial_vector_compressed_bytes(mode);
777 const size_t pcb = polynomial_compressed_bytes(mode);
778
779 if(buffer.size() != pvb + pcb) {
780 throw Decoding_Error("Kyber: unexpected ciphertext length");
781 }
782
783 auto pv = buffer.subspan(0, pvb);
784 auto p = buffer.subspan(pvb);
785
786 return Ciphertext(decompress_polynomial_vector(pv, mode), decompress_polynomial(p, mode), mode);
787 }
788
789 secure_vector<uint8_t> to_bytes() {
790 auto ct = compress(m_b, m_mode);
791 const auto p = compress(m_v, m_mode);
792 ct.insert(ct.end(), p.begin(), p.end());
793
794 return ct;
795 }
796
797 secure_vector<uint8_t> indcpa_decrypt(const PolynomialVector& polynomials) {
798 m_b.ntt();
799 auto mp = PolynomialVector::pointwise_acc_montgomery(polynomials, m_b);
800 mp.invntt_tomont();
801
802 mp -= m_v;
803 mp.reduce();
804 return mp.to_message();
805 }
806
807 private:
808 static size_t polynomial_vector_compressed_bytes(const KyberConstants& mode) {
809 const auto k = mode.k();
810 return (k == 2 || k == 3) ? k * 320 : k * 352;
811 }
812
813 static size_t polynomial_compressed_bytes(const KyberConstants& mode) {
814 const auto k = mode.k();
815 return (k == 2 || k == 3) ? 128 : 160;
816 }
817
818 static secure_vector<uint8_t> compress(PolynomialVector& pv, const KyberConstants& mode) {
819 secure_vector<uint8_t> r(polynomial_vector_compressed_bytes(mode));
820
821 pv.csubq();
822
823 if(mode.k() == 2 || mode.k() == 3) {
824 uint16_t t[4];
825 size_t offset = 0;
826 for(size_t i = 0; i < mode.k(); ++i) {
827 for(size_t j = 0; j < KyberConstants::N / 4; ++j) {
828 for(size_t k = 0; k < 4; ++k) {
829 t[k] = (((static_cast<uint32_t>(pv[i][4 * j + k]) << 10) + KyberConstants::Q / 2) /
830 KyberConstants::Q) &
831 0x3ff;
832 }
833
834 r[0 + offset] = static_cast<uint8_t>(t[0] >> 0);
835 r[1 + offset] = static_cast<uint8_t>((t[0] >> 8) | (t[1] << 2));
836 r[2 + offset] = static_cast<uint8_t>((t[1] >> 6) | (t[2] << 4));
837 r[3 + offset] = static_cast<uint8_t>((t[2] >> 4) | (t[3] << 6));
838 r[4 + offset] = static_cast<uint8_t>(t[3] >> 2);
839 offset += 5;
840 }
841 }
842 } else {
843 uint16_t t[8];
844 size_t offset = 0;
845 for(size_t i = 0; i < mode.k(); ++i) {
846 for(size_t j = 0; j < KyberConstants::N / 8; ++j) {
847 for(size_t k = 0; k < 8; ++k) {
848 t[k] = (((static_cast<uint32_t>(pv[i][8 * j + k]) << 11) + KyberConstants::Q / 2) /
849 KyberConstants::Q) &
850 0x7ff;
851 }
852
853 r[0 + offset] = static_cast<uint8_t>(t[0] >> 0);
854 r[1 + offset] = static_cast<uint8_t>((t[0] >> 8) | (t[1] << 3));
855 r[2 + offset] = static_cast<uint8_t>((t[1] >> 5) | (t[2] << 6));
856 r[3 + offset] = static_cast<uint8_t>(t[2] >> 2);
857 r[4 + offset] = static_cast<uint8_t>((t[2] >> 10) | (t[3] << 1));
858 r[5 + offset] = static_cast<uint8_t>((t[3] >> 7) | (t[4] << 4));
859 r[6 + offset] = static_cast<uint8_t>((t[4] >> 4) | (t[5] << 7));
860 r[7 + offset] = static_cast<uint8_t>(t[5] >> 1);
861 r[8 + offset] = static_cast<uint8_t>((t[5] >> 9) | (t[6] << 2));
862 r[9 + offset] = static_cast<uint8_t>((t[6] >> 6) | (t[7] << 5));
863 r[10 + offset] = static_cast<uint8_t>(t[7] >> 3);
864 offset += 11;
865 }
866 }
867 }
868
869 return r;
870 }
871
872 static secure_vector<uint8_t> compress(Polynomial& p, const KyberConstants& mode) {
873 secure_vector<uint8_t> r(polynomial_compressed_bytes(mode));
874
875 p.csubq();
876
877 uint8_t t[8];
878 if(mode.k() == 2 || mode.k() == 3) {
879 size_t offset = 0;
880 for(size_t i = 0; i < p.size() / 8; ++i) {
881 for(size_t j = 0; j < 8; ++j) {
882 t[j] = ct_int_div_kyber_q((static_cast<uint16_t>(p[8 * i + j]) << 4) + KyberConstants::Q / 2) & 15;
883 }
884
885 r[0 + offset] = t[0] | (t[1] << 4);
886 r[1 + offset] = t[2] | (t[3] << 4);
887 r[2 + offset] = t[4] | (t[5] << 4);
888 r[3 + offset] = t[6] | (t[7] << 4);
889 offset += 4;
890 }
891 } else if(mode.k() == 4) {
892 size_t offset = 0;
893 for(size_t i = 0; i < p.size() / 8; ++i) {
894 for(size_t j = 0; j < 8; ++j) {
895 t[j] = ct_int_div_kyber_q((static_cast<uint32_t>(p[8 * i + j]) << 5) + KyberConstants::Q / 2) & 31;
896 }
897
898 r[0 + offset] = (t[0] >> 0) | (t[1] << 5);
899 r[1 + offset] = (t[1] >> 3) | (t[2] << 2) | (t[3] << 7);
900 r[2 + offset] = (t[3] >> 1) | (t[4] << 4);
901 r[3 + offset] = (t[4] >> 4) | (t[5] << 1) | (t[6] << 6);
902 r[4 + offset] = (t[6] >> 2) | (t[7] << 3);
903 offset += 5;
904 }
905 }
906
907 return r;
908 }
909
910 static PolynomialVector decompress_polynomial_vector(std::span<const uint8_t> buffer,
911 const KyberConstants& mode) {
912 BOTAN_ASSERT(buffer.size() == polynomial_vector_compressed_bytes(mode),
913 "unexpected length of compressed polynomial vector");
914
915 PolynomialVector r(mode.k());
916 const uint8_t* a = buffer.data();
917
918 if(mode.k() == 4) {
919 uint16_t t[8];
920 for(size_t i = 0; i < mode.k(); ++i) {
921 for(size_t j = 0; j < KyberConstants::N / 8; ++j) {
922 t[0] = (a[0] >> 0) | (static_cast<uint16_t>(a[1]) << 8);
923 t[1] = (a[1] >> 3) | (static_cast<uint16_t>(a[2]) << 5);
924 t[2] = (a[2] >> 6) | (static_cast<uint16_t>(a[3]) << 2) | (static_cast<uint16_t>(a[4]) << 10);
925 t[3] = (a[4] >> 1) | (static_cast<uint16_t>(a[5]) << 7);
926 t[4] = (a[5] >> 4) | (static_cast<uint16_t>(a[6]) << 4);
927 t[5] = (a[6] >> 7) | (static_cast<uint16_t>(a[7]) << 1) | (static_cast<uint16_t>(a[8]) << 9);
928 t[6] = (a[8] >> 2) | (static_cast<uint16_t>(a[9]) << 6);
929 t[7] = (a[9] >> 5) | (static_cast<uint16_t>(a[10]) << 3);
930 a += 11;
931
932 for(size_t k = 0; k < 8; ++k) {
933 r[i][8 * j + k] = (static_cast<uint32_t>(t[k] & 0x7FF) * KyberConstants::Q + 1024) >> 11;
934 }
935 }
936 }
937 } else {
938 uint16_t t[4];
939 for(size_t i = 0; i < mode.k(); ++i) {
940 for(size_t j = 0; j < KyberConstants::N / 4; ++j) {
941 t[0] = (a[0] >> 0) | (static_cast<uint16_t>(a[1]) << 8);
942 t[1] = (a[1] >> 2) | (static_cast<uint16_t>(a[2]) << 6);
943 t[2] = (a[2] >> 4) | (static_cast<uint16_t>(a[3]) << 4);
944 t[3] = (a[3] >> 6) | (static_cast<uint16_t>(a[4]) << 2);
945 a += 5;
946
947 for(size_t k = 0; k < 4; ++k) {
948 r[i][4 * j + k] = (static_cast<uint32_t>(t[k] & 0x3FF) * KyberConstants::Q + 512) >> 10;
949 }
950 }
951 }
952 }
953
954 return r;
955 }
956
957 static Polynomial decompress_polynomial(std::span<const uint8_t> buffer, const KyberConstants& mode) {
958 BOTAN_ASSERT(buffer.size() == polynomial_compressed_bytes(mode), "unexpected length of compressed polynomial");
959
960 Polynomial r;
961 const uint8_t* a = buffer.data();
962
963 if(mode.k() == 4) {
964 uint8_t t[8];
965 for(size_t i = 0; i < KyberConstants::N / 8; ++i) {
966 t[0] = (a[0] >> 0);
967 t[1] = (a[0] >> 5) | (a[1] << 3);
968 t[2] = (a[1] >> 2);
969 t[3] = (a[1] >> 7) | (a[2] << 1);
970 t[4] = (a[2] >> 4) | (a[3] << 4);
971 t[5] = (a[3] >> 1);
972 t[6] = (a[3] >> 6) | (a[4] << 2);
973 t[7] = (a[4] >> 3);
974 a += 5;
975
976 for(size_t j = 0; j < 8; ++j) {
977 r[8 * i + j] = (static_cast<uint32_t>(t[j] & 31) * KyberConstants::Q + 16) >> 5;
978 }
979 }
980 } else {
981 for(size_t i = 0; i < KyberConstants::N / 2; ++i) {
982 r[2 * i + 0] = ((static_cast<uint16_t>(a[0] & 15) * KyberConstants::Q) + 8) >> 4;
983 r[2 * i + 1] = ((static_cast<uint16_t>(a[0] >> 4) * KyberConstants::Q) + 8) >> 4;
984 a += 1;
985 }
986 }
987
988 return r;
989 }
990
991 private:
992 KyberConstants m_mode;
993 PolynomialVector m_b;
994 Polynomial m_v;
995};
996
997} // anonymous namespace
998
999class Kyber_PublicKeyInternal {
1000 public:
1001 Kyber_PublicKeyInternal(KyberConstants mode, std::span<const uint8_t> polynomials, std::vector<uint8_t> seed) :
1002 m_mode(std::move(mode)),
1003 m_polynomials(PolynomialVector::from_bytes(polynomials, m_mode)),
1004 m_seed(std::move(seed)),
1005 m_public_key_bits_raw(concat(m_polynomials.to_bytes<std::vector<uint8_t>>(), m_seed)),
1006 m_H_public_key_bits_raw(m_mode.H()->process<std::vector<uint8_t>>(m_public_key_bits_raw)) {}
1007
1008 Kyber_PublicKeyInternal(KyberConstants mode, PolynomialVector polynomials, std::vector<uint8_t> seed) :
1009 m_mode(std::move(mode)),
1010 m_polynomials(std::move(polynomials)),
1011 m_seed(std::move(seed)),
1012 m_public_key_bits_raw(concat(m_polynomials.to_bytes<std::vector<uint8_t>>(), m_seed)),
1013 m_H_public_key_bits_raw(m_mode.H()->process<std::vector<uint8_t>>(m_public_key_bits_raw)) {}
1014
1015 const PolynomialVector& polynomials() const { return m_polynomials; }
1016
1017 const std::vector<uint8_t>& seed() const { return m_seed; }
1018
1019 const KyberConstants& mode() const { return m_mode; }
1020
1021 const std::vector<uint8_t>& public_key_bits_raw() const { return m_public_key_bits_raw; }
1022
1023 const std::vector<uint8_t>& H_public_key_bits_raw() const { return m_H_public_key_bits_raw; }
1024
1025 Kyber_PublicKeyInternal() = delete;
1026
1027 private:
1028 const KyberConstants m_mode;
1029 PolynomialVector m_polynomials;
1030 const std::vector<uint8_t> m_seed;
1031 const std::vector<uint8_t> m_public_key_bits_raw;
1032 const std::vector<uint8_t> m_H_public_key_bits_raw;
1033};
1034
1035class Kyber_PrivateKeyInternal {
1036 public:
1037 Kyber_PrivateKeyInternal(KyberConstants mode, PolynomialVector polynomials, secure_vector<uint8_t> z) :
1038 m_mode(std::move(mode)), m_polynomials(std::move(polynomials)), m_z(std::move(z)) {}
1039
1040 PolynomialVector& polynomials() { return m_polynomials; }
1041
1042 const secure_vector<uint8_t>& z() const { return m_z; }
1043
1044 const KyberConstants& mode() const { return m_mode; }
1045
1046 Kyber_PrivateKeyInternal() = delete;
1047
1048 private:
1049 KyberConstants m_mode;
1050 PolynomialVector m_polynomials;
1051 secure_vector<uint8_t> m_z;
1052};
1053
1054class Kyber_KEM_Cryptor {
1055 protected:
1056 Kyber_KEM_Cryptor(std::shared_ptr<const Kyber_PublicKeyInternal> public_key) :
1057 m_public_key(std::move(public_key)),
1058 m_mode(m_public_key->mode()),
1059 m_at(PolynomialMatrix::generate(m_public_key->seed(), true, m_mode)) {}
1060
1061 secure_vector<uint8_t> indcpa_enc(std::span<const uint8_t> m, std::span<const uint8_t> coins) {
1062 auto sp = PolynomialVector::getnoise_eta1(coins, 0, m_mode);
1063 auto ep = PolynomialVector::getnoise_eta2(coins, m_mode.k(), m_mode);
1064 auto epp = Polynomial::getnoise_eta2(coins, 2 * m_mode.k(), m_mode);
1065
1066 auto k = Polynomial::from_message(m);
1067
1068 sp.ntt();
1069
1070 // matrix-vector multiplication
1071 auto bp = m_at.pointwise_acc_montgomery(sp);
1072 auto v = PolynomialVector::pointwise_acc_montgomery(m_public_key->polynomials(), sp);
1073
1074 bp.invntt_tomont();
1075 v.invntt_tomont();
1076
1077 bp += ep;
1078 v += epp;
1079 v += k;
1080 bp.reduce();
1081 v.reduce();
1082
1083 return Ciphertext(std::move(bp), v, m_mode).to_bytes();
1084 }
1085
1086 const KyberConstants& mode() const { return m_mode; }
1087
1088 private:
1089 std::shared_ptr<const Kyber_PublicKeyInternal> m_public_key;
1090 const KyberConstants& m_mode;
1091 const PolynomialMatrix m_at;
1092};
1093
1094namespace {
1095
1096size_t kyber_key_length_to_encap_key_length(size_t kl) {
1097 switch(kl) {
1098 case 800:
1099 return 768;
1100 case 1184:
1101 return 1088;
1102 case 1568:
1103 return 1568;
1104 default:
1105 throw Internal_Error("Unexpected Kyber key length");
1106 }
1107}
1108
1109} // namespace
1110
1111class Kyber_KEM_Encryptor final : public PK_Ops::KEM_Encryption_with_KDF,
1112 protected Kyber_KEM_Cryptor {
1113 public:
1114 Kyber_KEM_Encryptor(const Kyber_PublicKey& key, std::string_view kdf) :
1115 KEM_Encryption_with_KDF(kdf), Kyber_KEM_Cryptor(key.m_public), m_key(key) {}
1116
1117 size_t raw_kem_shared_key_length() const override { return 32; }
1118
1119 size_t encapsulated_key_length() const override {
1120 return kyber_key_length_to_encap_key_length(m_key.key_length());
1121 }
1122
1123 void raw_kem_encrypt(std::span<uint8_t> out_encapsulated_key,
1124 std::span<uint8_t> out_shared_key,
1125 RandomNumberGenerator& rng) override {
1126 // naming from kyber spec
1127 auto H = mode().H();
1128 auto G = mode().G();
1129 auto KDF = mode().KDF();
1130
1131 H->update(rng.random_vec(KyberConstants::kSymBytes));
1132 const auto shared_secret = H->final();
1133
1134 // Multitarget countermeasure for coins + contributory KEM
1135 G->update(shared_secret);
1136 G->update(m_key.H_public_key_bits_raw());
1137 const auto g_out = G->final();
1138
1139 BOTAN_ASSERT_EQUAL(g_out.size(), 64, "Expected output length of Kyber G");
1140
1141 const auto lower_g_out = std::span(g_out).subspan(0, 32);
1142 const auto upper_g_out = std::span(g_out).subspan(32, 32);
1143
1144 const auto encapsulation = indcpa_enc(shared_secret, upper_g_out);
1145
1146 // TODO: avoid copy by letting Ciphertext write straight into std::span<>
1147 BOTAN_ASSERT_NOMSG(encapsulation.size() == out_encapsulated_key.size());
1148 std::copy(encapsulation.begin(), encapsulation.end(), out_encapsulated_key.begin());
1149
1150 KDF->update(lower_g_out.data(), lower_g_out.size());
1151 KDF->update(H->process(out_encapsulated_key));
1152 KDF->final(out_shared_key);
1153 }
1154
1155 private:
1156 const Kyber_PublicKey& m_key;
1157};
1158
1159class Kyber_KEM_Decryptor final : public PK_Ops::KEM_Decryption_with_KDF,
1160 protected Kyber_KEM_Cryptor {
1161 public:
1162 Kyber_KEM_Decryptor(const Kyber_PrivateKey& key, std::string_view kdf) :
1163 PK_Ops::KEM_Decryption_with_KDF(kdf), Kyber_KEM_Cryptor(key.m_public), m_key(key) {}
1164
1165 size_t raw_kem_shared_key_length() const override { return 32; }
1166
1167 size_t encapsulated_key_length() const override {
1168 return kyber_key_length_to_encap_key_length(m_key.key_length());
1169 }
1170
1171 void raw_kem_decrypt(std::span<uint8_t> out_shared_key, std::span<const uint8_t> encapsulated_key) override {
1172 // naming from kyber spec
1173 auto H = mode().H();
1174 auto G = mode().G();
1175 auto KDF = mode().KDF();
1176
1177 const auto shared_secret = indcpa_dec(encapsulated_key);
1178
1179 // Multitarget countermeasure for coins + contributory KEM
1180 G->update(shared_secret);
1181 G->update(m_key.H_public_key_bits_raw());
1182
1183 const auto g_out = G->final();
1184
1185 BOTAN_ASSERT_EQUAL(g_out.size(), 64, "Expected output length of Kyber G");
1186
1187 const auto lower_g_out = std::span(g_out).subspan(0, 32);
1188 const auto upper_g_out = std::span(g_out).subspan(32, 32);
1189
1190 H->update(encapsulated_key);
1191
1192 const auto cmp = indcpa_enc(shared_secret, upper_g_out);
1193 BOTAN_ASSERT(encapsulated_key.size() == cmp.size(), "output of indcpa_enc has unexpected length");
1194
1195 // Overwrite pre-k with z on re-encryption failure (constant time)
1196 secure_vector<uint8_t> lower_g_out_final(lower_g_out.size());
1197 BOTAN_ASSERT_NOMSG(lower_g_out.size() == m_key.m_private->z().size());
1198
1199 const auto reencrypt_success = CT::is_equal(encapsulated_key.data(), cmp.data(), encapsulated_key.size());
1200 CT::conditional_copy_mem(reencrypt_success,
1201 lower_g_out_final.data(),
1202 lower_g_out.data(),
1203 m_key.m_private->z().data(),
1204 lower_g_out_final.size());
1205
1206 KDF->update(lower_g_out_final);
1207 KDF->update(H->final());
1208 KDF->final(out_shared_key);
1209 }
1210
1211 private:
1212 secure_vector<uint8_t> indcpa_dec(std::span<const uint8_t> c) {
1213 auto ct = Ciphertext::from_bytes(c, mode());
1214 return ct.indcpa_decrypt(m_key.m_private->polynomials());
1215 }
1216
1217 private:
1218 const Kyber_PrivateKey& m_key;
1219};
1220
1222 return m_public->mode().mode();
1223}
1224
1228
1232
1234 return m_public->mode().estimated_strength();
1235}
1236
1237std::shared_ptr<Kyber_PublicKeyInternal> Kyber_PublicKey::initialize_from_encoding(std::span<const uint8_t> pub_key,
1238 KyberMode m) {
1239 KyberConstants mode(m);
1240
1241 if(pub_key.size() != mode.public_key_byte_length()) {
1242 throw Invalid_Argument("kyber public key does not have the correct byte count");
1243 }
1244
1245 BufferSlicer s(pub_key);
1246
1247 auto poly_vec = s.take(mode.polynomial_vector_byte_length());
1248 auto seed = s.copy_as_vector(KyberConstants::kSeedLength);
1250
1251 return std::make_shared<Kyber_PublicKeyInternal>(std::move(mode), poly_vec, std::move(seed));
1252}
1253
1254Kyber_PublicKey::Kyber_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
1255 Kyber_PublicKey(key_bits, KyberMode(alg_id.oid())) {}
1256
1257Kyber_PublicKey::Kyber_PublicKey(std::span<const uint8_t> pub_key, KyberMode m) :
1258 m_public(initialize_from_encoding(pub_key, m)) {}
1259
1261 m_public(std::make_shared<Kyber_PublicKeyInternal>(*other.m_public)) {}
1262
1263std::vector<uint8_t> Kyber_PublicKey::public_key_bits() const {
1264 return public_key_bits_raw();
1265}
1266
1267const std::vector<uint8_t>& Kyber_PublicKey::public_key_bits_raw() const {
1268 return m_public->public_key_bits_raw();
1269}
1270
1271const std::vector<uint8_t>& Kyber_PublicKey::H_public_key_bits_raw() const {
1272 return m_public->H_public_key_bits_raw();
1273}
1274
1276 return m_public->mode().public_key_byte_length();
1277}
1278
1280 return true; // ??
1281}
1282
1283std::unique_ptr<Private_Key> Kyber_PublicKey::generate_another(RandomNumberGenerator& rng) const {
1284 return std::make_unique<Kyber_PrivateKey>(rng, mode());
1285}
1286
1288 KyberConstants mode(m);
1289
1290 auto G = mode.G();
1291 auto seed = G->process(rng.random_vec(KyberConstants::kSymBytes));
1292
1293 const auto middle = G->output_length() / 2;
1294
1295 BufferSlicer s(seed);
1296 auto seed1 = s.copy_as_vector(middle);
1297 auto seed2 = s.take(middle);
1299
1300 auto a = PolynomialMatrix::generate(seed1, false, mode);
1301 auto skpv = PolynomialVector::getnoise_eta1(seed2, 0, mode);
1302 auto e = PolynomialVector::getnoise_eta1(seed2, mode.k(), mode);
1303
1304 skpv.ntt();
1305 e.ntt();
1306
1307 // matrix-vector multiplication
1308 auto pkpv = a.pointwise_acc_montgomery(skpv, true);
1309 pkpv += e;
1310 pkpv.reduce();
1311
1312 m_public = std::make_shared<Kyber_PublicKeyInternal>(mode, std::move(pkpv), std::move(seed1));
1313 m_private = std::make_shared<Kyber_PrivateKeyInternal>(
1314 std::move(mode), std::move(skpv), rng.random_vec(KyberConstants::kZLength));
1315}
1316
1317Kyber_PrivateKey::Kyber_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
1318 Kyber_PrivateKey(key_bits, KyberMode(alg_id.oid())) {}
1319
1320Kyber_PrivateKey::Kyber_PrivateKey(std::span<const uint8_t> sk, KyberMode m) {
1321 KyberConstants mode(m);
1322
1323 if(mode.private_key_byte_length() != sk.size()) {
1324 throw Invalid_Argument("kyber private key does not have the correct byte count");
1325 }
1326
1327 BufferSlicer s(sk);
1328
1329 auto skpv = PolynomialVector::from_bytes(s.take(mode.polynomial_vector_byte_length()), mode);
1330 auto pub_key = s.take(mode.public_key_byte_length());
1331 s.skip(KyberConstants::kPublicKeyHashLength);
1332 auto z = s.copy_as_secure_vector(KyberConstants::kZLength);
1333
1335
1336 m_public = initialize_from_encoding(pub_key, m);
1337 m_private = std::make_shared<Kyber_PrivateKeyInternal>(std::move(mode), std::move(skpv), std::move(z));
1338
1339 BOTAN_ASSERT(m_private && m_public, "reading private key encoding");
1340}
1341
1342std::unique_ptr<Public_Key> Kyber_PrivateKey::public_key() const {
1343 return std::make_unique<Kyber_PublicKey>(*this);
1344}
1345
1349
1351 return concat(m_private->polynomials().to_bytes<secure_vector<uint8_t>>(),
1354 m_private->z());
1355}
1356
1357std::unique_ptr<PK_Ops::KEM_Encryption> Kyber_PublicKey::create_kem_encryption_op(std::string_view params,
1358 std::string_view provider) const {
1359 if(provider.empty() || provider == "base") {
1360 return std::make_unique<Kyber_KEM_Encryptor>(*this, params);
1361 }
1362 throw Provider_Not_Found(algo_name(), provider);
1363}
1364
1365std::unique_ptr<PK_Ops::KEM_Decryption> Kyber_PrivateKey::create_kem_decryption_op(RandomNumberGenerator& rng,
1366 std::string_view params,
1367 std::string_view provider) const {
1368 BOTAN_UNUSED(rng);
1369 if(provider.empty() || provider == "base") {
1370 return std::make_unique<Kyber_KEM_Decryptor>(*this, params);
1371 }
1372 throw Provider_Not_Found(algo_name(), provider);
1373}
1374
1375} // namespace Botan
#define BOTAN_UNUSED
Definition assert.h:118
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:98
#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)
Definition assert.h:68
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
#define BOTAN_ASSERT_UNREACHABLE()
Definition assert.h:137
void skip(const size_t count)
Definition stl_util.h:183
auto copy_as_secure_vector(const size_t count)
Definition stl_util.h:154
bool empty() const
Definition stl_util.h:187
std::span< const uint8_t > take(const size_t count)
Definition stl_util.h:156
auto copy_as_vector(const size_t count)
Definition stl_util.h:152
bool is_available() const
Definition kyber.cpp:136
std::string to_string() const
Definition kyber.cpp:109
bool is_modern() const
Definition kyber.cpp:132
OID object_identifier() const
Definition kyber.cpp:105
KyberMode(Mode mode)
Definition kyber.cpp:99
bool is_90s() const
Definition kyber.cpp:128
Mode mode() const
Definition kyber.h:56
std::unique_ptr< PK_Ops::KEM_Decryption > create_kem_decryption_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition kyber.cpp:1365
std::unique_ptr< Public_Key > public_key() const override
Definition kyber.cpp:1342
Kyber_PrivateKey(RandomNumberGenerator &rng, KyberMode mode)
Definition kyber.cpp:1287
secure_vector< uint8_t > raw_private_key_bits() const override
Definition kyber.cpp:1346
secure_vector< uint8_t > private_key_bits() const override
Definition kyber.cpp:1350
static std::shared_ptr< Kyber_PublicKeyInternal > initialize_from_encoding(std::span< const uint8_t > pub_key, KyberMode m)
Definition kyber.cpp:1237
std::vector< uint8_t > public_key_bits() const override
Definition kyber.cpp:1263
bool check_key(RandomNumberGenerator &, bool) const override
Definition kyber.cpp:1279
std::string algo_name() const override
Definition kyber.h:87
std::shared_ptr< Kyber_PublicKeyInternal > m_public
Definition kyber.h:125
size_t key_length() const override
Definition kyber.cpp:1275
AlgorithmIdentifier algorithm_identifier() const override
Definition kyber.cpp:1225
const std::vector< uint8_t > & H_public_key_bits_raw() const
Definition kyber.cpp:1271
std::unique_ptr< PK_Ops::KEM_Encryption > create_kem_encryption_op(std::string_view params, std::string_view provider) const override
Definition kyber.cpp:1357
const std::vector< uint8_t > & public_key_bits_raw() const
Definition kyber.cpp:1267
KyberMode mode() const
Definition kyber.cpp:1221
OID object_identifier() const override
Definition kyber.cpp:1229
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
Definition kyber.cpp:1283
size_t estimated_strength() const override
Definition kyber.cpp:1233
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:74
KEM_Decryption_with_KDF(std::string_view kdf)
Definition pk_ops.cpp:223
KEM_Encryption_with_KDF(std::string_view kdf)
Definition pk_ops.cpp:190
void random_vec(std::span< uint8_t > v)
Definition rng.h:179
int(* final)(unsigned char *, CTX *)
FE_25519 T
Definition ge.cpp:34
constexpr Mask< T > conditional_copy_mem(Mask< T > mask, T *to, const T *from0, const T *from1, size_t elems)
Definition ct_utils.h:292
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:345
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
RetT reduce(const std::vector< KeyT > &keys, RetT acc, ReducerT reducer)
Definition stl_util.h:48
constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3)
Definition loadstor.h:100
std::vector< T, Alloc > & operator+=(std::vector< T, Alloc > &out, const std::vector< T, Alloc2 > &in)
Definition secmem.h:80
constexpr auto operator-=(Strong< T1, Tags... > &a, T2 b)
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
decltype(auto) concat(Ts &&... buffers)
Definition stl_util.h:258