Botan 3.5.0
Crypto and TLS for C&
kyber_structures.h
Go to the documentation of this file.
1/*
2 * Crystals Kyber Structures
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-2024 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 * (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity
11 *
12 * Botan is released under the Simplified BSD License (see license.txt)
13 */
14
15#ifndef BOTAN_KYBER_STRUCTURES_H_
16#define BOTAN_KYBER_STRUCTURES_H_
17
18#include <botan/exceptn.h>
19#include <botan/xof.h>
20
21#include <botan/internal/ct_utils.h>
22#include <botan/internal/kyber_constants.h>
23#include <botan/internal/kyber_symmetric_primitives.h>
24#include <botan/internal/kyber_types.h>
25#include <botan/internal/loadstor.h>
26#include <botan/internal/stl_util.h>
27
28#include <span>
29
30namespace Botan {
31
32namespace detail {
33
34/**
35 * Constant time implementation for computing an unsigned integer division
36 * with KyberConstants::Q = 3329.
37 *
38 * It enforces the optimization of various compilers,
39 * replacing the division operation with multiplication and shifts.
40 *
41 * This implementation is only valid for integers <= 2**20
42 *
43 * @returns (a / KyberConstants::Q)
44 */
45inline constexpr uint16_t ct_int_div_kyber_q(uint32_t a) {
46 BOTAN_DEBUG_ASSERT(a < (1 << 18));
47
48 /*
49 Constants based on "Hacker's Delight" (Second Edition) by Henry
50 S. Warren, Jr. Chapter 10-9 "Unsigned Division by Divisors >= 1"
51 */
52 const uint64_t m = 161271;
53 const size_t p = 29;
54 return static_cast<uint16_t>((a * m) >> p);
55}
56
57} // namespace detail
58
60 public:
61 Polynomial() : m_coeffs({0}) {}
62
63 /**
64 * Applies conditional subtraction of q to each coefficient of the polynomial.
65 */
66 void csubq() {
67 for(auto& coeff : m_coeffs) {
68 coeff -= KyberConstants::Q;
69 coeff += (coeff >> 15) & KyberConstants::Q;
70 }
71 }
72
73 /**
74 * Applies Barrett reduction to all coefficients of the polynomial
75 */
76 void reduce() {
77 for(auto& c : m_coeffs) {
78 c = barrett_reduce(c);
79 }
80 }
81
82 void to_bytes(std::span<uint8_t> out) {
83 this->csubq();
84
85 BufferStuffer bs(out);
86 for(size_t i = 0; i < size() / 2; ++i) {
87 const uint16_t t0 = m_coeffs[2 * i];
88 const uint16_t t1 = m_coeffs[2 * i + 1];
89 auto buf = bs.next<3>();
90 buf[0] = static_cast<uint8_t>(t0 >> 0);
91 buf[1] = static_cast<uint8_t>((t0 >> 8) | (t1 << 4));
92 buf[2] = static_cast<uint8_t>(t1 >> 4);
93 }
95 }
96
97 /**
98 * Given an array of uniformly random bytes, compute polynomial with coefficients
99 * distributed according to a centered binomial distribution with parameter eta=2
100 */
102 Polynomial r;
103
104 BOTAN_ASSERT(buf.size() == (2 * r.size() / 4), "wrong input buffer size for cbd2");
105
106 BufferSlicer bs(buf);
107 for(size_t i = 0; i < r.size() / 8; ++i) {
108 uint32_t t = load_le(bs.take<4>());
109 uint32_t d = t & 0x55555555;
110 d += (t >> 1) & 0x55555555;
111
112 for(size_t j = 0; j < 8; ++j) {
113 int16_t a = (d >> (4 * j + 0)) & 0x3;
114 int16_t b = (d >> (4 * j + 2)) & 0x3;
115 r.m_coeffs[8 * i + j] = a - b;
116 }
117 }
119
120 return r;
121 }
122
123 /**
124 * Given an array of uniformly random bytes, compute polynomial with coefficients
125 * distributed according to a centered binomial distribution with parameter eta=3
126 *
127 * This function is only needed for Kyber-512
128 */
130 Polynomial r;
131
132 BOTAN_ASSERT(buf.size() == (3 * r.size() / 4), "wrong input buffer size for cbd3");
133
134 // Note: load_le<> does not support loading a 3-byte value
135 const auto load_le = [](std::span<const uint8_t, 3> in) { return make_uint32(0, in[2], in[1], in[0]); };
136
137 BufferSlicer bs(buf);
138 for(size_t i = 0; i < r.size() / 4; ++i) {
139 uint32_t t = load_le(bs.take<3>());
140 uint32_t d = t & 0x00249249;
141 d += (t >> 1) & 0x00249249;
142 d += (t >> 2) & 0x00249249;
143
144 for(size_t j = 0; j < 4; ++j) {
145 int16_t a = (d >> (6 * j + 0)) & 0x7;
146 int16_t b = (d >> (6 * j + 3)) & 0x7;
147 r.m_coeffs[4 * i + j] = a - b;
148 }
149 }
151
152 return r;
153 }
154
155 /**
156 * Sample a polynomial deterministically from a seed and a nonce, with output
157 * polynomial close to centered binomial distribution with parameter eta=2.
158 */
160 uint8_t nonce,
161 const KyberConstants& mode) {
162 const auto eta2 = mode.eta2();
163 BOTAN_ASSERT(eta2 == 2, "Invalid eta2 value");
164
165 const auto outlen = eta2 * KyberConstants::N / 4;
166 return Polynomial::cbd2(mode.symmetric_primitives().PRF(seed, nonce, outlen));
167 }
168
169 /**
170 * Sample a polynomial deterministically from a seed and a nonce, with output
171 * polynomial close to centered binomial distribution with parameter mode.eta1()
172 */
174 uint8_t nonce,
175 const KyberConstants& mode) {
176 const auto eta1 = mode.eta1();
177 BOTAN_ASSERT(eta1 == 2 || eta1 == 3, "Invalid eta1 value");
178
179 const auto outlen = eta1 * KyberConstants::N / 4;
180 return (eta1 == 2) ? Polynomial::cbd2(mode.symmetric_primitives().PRF(seed, nonce, outlen))
181 : Polynomial::cbd3(mode.symmetric_primitives().PRF(seed, nonce, outlen));
182 }
183
184 static Polynomial from_bytes(std::span<const uint8_t> a) {
185 Polynomial r;
186 for(size_t i = 0; i < r.size() / 2; ++i) {
187 r.m_coeffs[2 * i] = ((a[3 * i + 0] >> 0) | (static_cast<uint16_t>(a[3 * i + 1]) << 8)) & 0xFFF;
188 r.m_coeffs[2 * i + 1] = ((a[3 * i + 1] >> 4) | (static_cast<uint16_t>(a[3 * i + 2]) << 4)) & 0xFFF;
189 }
190 return r;
191 }
192
194 BOTAN_ASSERT(msg.size() == KyberConstants::N / 8, "message length must be Kyber_N/8 bytes");
195
196 Polynomial r;
197 for(size_t i = 0; i < r.size() / 8; ++i) {
198 for(size_t j = 0; j < 8; ++j) {
199 const auto mask = CT::Mask<uint16_t>::is_zero((msg[i] >> j) & 1);
200 r.m_coeffs[8 * i + j] = mask.if_not_set_return((KyberConstants::Q + 1) / 2);
201 }
202 }
203 return r;
204 }
205
207 KyberMessage result(size() / 8);
208
209 this->csubq();
210
211 for(size_t i = 0; i < size() / 8; ++i) {
212 result[i] = 0;
213 for(size_t j = 0; j < 8; ++j) {
214 const uint16_t t = detail::ct_int_div_kyber_q((static_cast<uint16_t>(this->m_coeffs[8 * i + j]) << 1) +
216 result[i] |= (t & 1) << j;
217 }
218 }
219
220 return result;
221 }
222
223 /**
224 * Adds two polynomials element-wise. Does not perform a reduction after the addition.
225 * Therefore this operation might cause an integer overflow.
226 */
228 for(size_t i = 0; i < this->size(); ++i) {
229 BOTAN_DEBUG_ASSERT(static_cast<int32_t>(this->m_coeffs[i]) + other.m_coeffs[i] <=
230 std::numeric_limits<int16_t>::max());
231 this->m_coeffs[i] = this->m_coeffs[i] + other.m_coeffs[i];
232 }
233 return *this;
234 }
235
236 /**
237 * Subtracts two polynomials element-wise. Does not perform a reduction after the subtraction.
238 * Therefore this operation might cause an integer underflow.
239 */
241 for(size_t i = 0; i < this->size(); ++i) {
242 BOTAN_DEBUG_ASSERT(static_cast<int32_t>(other.m_coeffs[i]) - this->m_coeffs[i] >=
243 std::numeric_limits<int16_t>::min());
244 this->m_coeffs[i] = other.m_coeffs[i] - this->m_coeffs[i];
245 }
246 return *this;
247 }
248
249 /**
250 * Multiplication of two polynomials in NTT domain
251 */
253 /**
254 * Multiplication of polynomials in Zq[X]/(X^2-zeta) used for
255 * multiplication of elements in Rq in NTT domain.
256 */
257 auto basemul = [](int16_t r[2], const int16_t s[2], const int16_t t[2], const int16_t zeta) {
258 r[0] = fqmul(s[1], t[1]);
259 r[0] = fqmul(r[0], zeta);
260 r[0] += fqmul(s[0], t[0]);
261
262 r[1] = fqmul(s[0], t[1]);
263 r[1] += fqmul(s[1], t[0]);
264 };
265
266 Polynomial r;
267
268 for(size_t i = 0; i < r.size() / 4; ++i) {
269 basemul(&r.m_coeffs[4 * i], &a.m_coeffs[4 * i], &b.m_coeffs[4 * i], KyberConstants::zetas[64 + i]);
270 basemul(
271 &r.m_coeffs[4 * i + 2], &a.m_coeffs[4 * i + 2], &b.m_coeffs[4 * i + 2], -KyberConstants::zetas[64 + i]);
272 }
273
274 return r;
275 }
276
277 /**
278 * Run rejection sampling on uniform random bytes to generate uniform
279 * random integers mod q.
280 */
281 static Polynomial sample_rej_uniform(std::unique_ptr<XOF> xof) {
282 Polynomial p;
283
284 size_t count = 0;
285 while(count < p.size()) {
286 std::array<uint8_t, 3> buf;
287 xof->output(buf);
288
289 const uint16_t val0 = ((buf[0] >> 0) | (static_cast<uint16_t>(buf[1]) << 8)) & 0xFFF;
290 const uint16_t val1 = ((buf[1] >> 4) | (static_cast<uint16_t>(buf[2]) << 4)) & 0xFFF;
291
292 if(val0 < KyberConstants::Q) {
293 p.m_coeffs[count++] = val0;
294 }
295 if(count < p.size() && val1 < KyberConstants::Q) {
296 p.m_coeffs[count++] = val1;
297 }
298 }
299
300 return p;
301 }
302
303 /**
304 * Inplace conversion of all coefficients of a polynomial from normal
305 * domain to Montgomery domain.
306 */
307 void tomont() {
308 constexpr int16_t f = (1ULL << 32) % KyberConstants::Q;
309 for(auto& c : m_coeffs) {
310 c = montgomery_reduce(static_cast<int32_t>(c) * f);
311 }
312 }
313
314 /**
315 * Computes negacyclic number-theoretic transform (NTT) of a polynomial in place;
316 * inputs assumed to be in normal order, output in bitreversed order.
317 */
318 void ntt() {
319 for(size_t len = size() / 2, k = 0; len >= 2; len /= 2) {
320 for(size_t start = 0, j = 0; start < size(); start = j + len) {
321 const auto zeta = KyberConstants::zetas[++k];
322 for(j = start; j < start + len; ++j) {
323 const auto t = fqmul(zeta, m_coeffs[j + len]);
324 m_coeffs[j + len] = m_coeffs[j] - t;
325 m_coeffs[j] = m_coeffs[j] + t;
326 }
327 }
328 }
329
330 reduce();
331 }
332
333 /**
334 * Computes inverse of negacyclic number-theoretic transform (NTT) of a polynomial
335 * in place; inputs assumed to be in bitreversed order, output in normal order.
336 */
338 for(size_t len = 2, k = 0; len <= size() / 2; len *= 2) {
339 for(size_t start = 0, j = 0; start < size(); start = j + len) {
340 const auto zeta = KyberConstants::zetas_inv[k++];
341 for(j = start; j < start + len; ++j) {
342 const auto t = m_coeffs[j];
343 m_coeffs[j] = barrett_reduce(t + m_coeffs[j + len]);
344 m_coeffs[j + len] = fqmul(zeta, t - m_coeffs[j + len]);
345 }
346 }
347 }
348
349 for(auto& c : m_coeffs) {
350 c = fqmul(c, KyberConstants::zetas_inv[127]);
351 }
352 }
353
354 size_t size() const { return m_coeffs.size(); }
355
356 int16_t operator[](size_t idx) const { return m_coeffs[idx]; }
357
358 int16_t& operator[](size_t idx) { return m_coeffs[idx]; }
359
360 private:
361 /**
362 * Barrett reduction; given a 16-bit integer a, computes 16-bit integer congruent
363 * to a mod q in {0,...,q}.
364 */
365 static int16_t barrett_reduce(int16_t a) {
366 constexpr int32_t v = ((1U << 26) + KyberConstants::Q / 2) / KyberConstants::Q;
367 const int16_t t = (v * a >> 26) * KyberConstants::Q;
368 return a - t;
369 }
370
371 /**
372 * Multiplication followed by Montgomery reduction.
373 */
374 static int16_t fqmul(int16_t a, int16_t b) { return montgomery_reduce(static_cast<int32_t>(a) * b); }
375
376 /**
377 * Montgomery reduction; given a 32-bit integer a, computes 16-bit integer
378 * congruent to a * R^-1 mod q, where R=2^16
379 */
380 static int16_t montgomery_reduce(int32_t a) {
381 const int16_t u = static_cast<int16_t>(a * KyberConstants::Q_Inv);
382 int32_t t = static_cast<int32_t>(u) * KyberConstants::Q;
383 t = a - t;
384 t >>= 16;
385 return static_cast<int16_t>(t);
386 }
387
388 std::array<int16_t, KyberConstants::N> m_coeffs;
389};
390
392 public:
394
395 explicit PolynomialVector(const size_t k) : m_vec(k) {}
396
397 public:
398 static PolynomialVector from_bytes(std::span<const uint8_t> a, const KyberConstants& mode) {
399 BOTAN_ASSERT(a.size() == mode.polynomial_vector_byte_length(), "wrong byte length for frombytes");
400
401 PolynomialVector r(mode.k());
402
403 BufferSlicer bs(a);
404 for(size_t i = 0; i < mode.k(); ++i) {
406 }
408
409 return r;
410 }
411
412 /**
413 * Pointwise multiply elements of a and b, accumulate into r, and multiply by 2^-16.
414 */
416 BOTAN_ASSERT(a.m_vec.size() == b.m_vec.size(),
417 "pointwise_acc_montgomery works on equally sized "
418 "PolynomialVectors only");
419
420 Polynomial r;
421 for(size_t i = 0; i < a.m_vec.size(); ++i) {
422 r += Polynomial::basemul_montgomery(a.m_vec[i], b.m_vec[i]);
423 }
424 r.reduce();
425 return r;
426 }
427
429 uint8_t nonce,
430 const KyberConstants& mode) {
431 PolynomialVector r(mode.k());
432 for(auto& p : r.m_vec) {
433 p = Polynomial::getnoise_eta2(seed, nonce++, mode);
434 }
435 return r;
436 }
437
439 uint8_t nonce,
440 const KyberConstants& mode) {
441 PolynomialVector r(mode.k());
442 for(auto& p : r.m_vec) {
443 p = Polynomial::getnoise_eta1(seed, nonce++, mode);
444 }
445 return r;
446 }
447
448 template <concepts::resizable_byte_buffer T = secure_vector<uint8_t>>
451
452 BufferStuffer bs(r);
453 for(auto& v : m_vec) {
455 }
457
458 return r;
459 }
460
461 /**
462 * Applies conditional subtraction of q to each coefficient of each element
463 * of the vector of polynomials.
464 */
465 void csubq() {
466 for(auto& p : m_vec) {
467 p.csubq();
468 }
469 }
470
472 BOTAN_ASSERT(m_vec.size() == other.m_vec.size(), "cannot add polynomial vectors of differing lengths");
473
474 for(size_t i = 0; i < m_vec.size(); ++i) {
475 m_vec[i] += other.m_vec[i];
476 }
477 return *this;
478 }
479
480 Polynomial& operator[](size_t idx) { return m_vec[idx]; }
481
482 /**
483 * Applies Barrett reduction to each coefficient of each element of a vector of polynomials.
484 */
485 void reduce() {
486 for(auto& v : m_vec) {
487 v.reduce();
488 }
489 }
490
491 /**
492 * Apply inverse NTT to all elements of a vector of polynomials and multiply by Montgomery factor 2^16.
493 */
495 for(auto& v : m_vec) {
496 v.invntt_tomont();
497 }
498 }
499
500 /**
501 * Apply forward NTT to all elements of a vector of polynomials.
502 */
503 void ntt() {
504 for(auto& v : m_vec) {
505 v.ntt();
506 }
507 }
508
509 private:
510 std::vector<Polynomial> m_vec;
511};
512
514 public:
516
518 const bool transposed,
519 const KyberConstants& mode) {
520 BOTAN_ASSERT(seed.size() == KyberConstants::kSymBytes, "unexpected seed size");
521
522 PolynomialMatrix matrix(mode);
523
524 for(uint8_t i = 0; i < mode.k(); ++i) {
525 for(uint8_t j = 0; j < mode.k(); ++j) {
526 const auto pos = (transposed) ? std::tuple(i, j) : std::tuple(j, i);
527 matrix.m_mat[i][j] = Polynomial::sample_rej_uniform(mode.symmetric_primitives().XOF(seed, pos));
528 }
529 }
530
531 return matrix;
532 }
533
534 PolynomialVector pointwise_acc_montgomery(const PolynomialVector& vec, const bool with_mont = false) const {
535 PolynomialVector result(m_mat.size());
536
537 for(size_t i = 0; i < m_mat.size(); ++i) {
538 result[i] = PolynomialVector::pointwise_acc_montgomery(m_mat[i], vec);
539 if(with_mont) {
540 result[i].tomont();
541 }
542 }
543
544 return result;
545 }
546
547 private:
548 explicit PolynomialMatrix(const KyberConstants& mode) : m_mat(mode.k(), PolynomialVector(mode.k())) {}
549
550 private:
551 std::vector<PolynomialVector> m_mat;
552};
553
555 public:
556 Ciphertext() = delete;
557
559 m_mode(std::move(mode)), m_b(std::move(b)), m_v(v) {}
560
562 const size_t pvb = mode.polynomial_vector_compressed_bytes();
563 const size_t pcb = mode.polynomial_compressed_bytes();
564
565 if(buffer.size() != pvb + pcb) {
566 throw Decoding_Error("Kyber: unexpected ciphertext length");
567 }
568
569 BufferSlicer bs(buffer);
570 auto pv = bs.take(pvb);
571 auto p = bs.take(pcb);
573
574 return Ciphertext(decompress_polynomial_vector(pv, mode), decompress_polynomial(p, mode), mode);
575 }
576
578 BufferStuffer bs(out);
579 compress(bs.next(m_mode.polynomial_vector_compressed_bytes()), m_b, m_mode);
580 compress(bs.next(m_mode.polynomial_compressed_bytes()), m_v, m_mode);
582 }
583
589
590 PolynomialVector& b() { return m_b; }
591
592 Polynomial& v() { return m_v; }
593
594 private:
595 static void compress(std::span<uint8_t> out, PolynomialVector& pv, const KyberConstants& mode) {
596 pv.csubq();
597
598 BufferStuffer bs(out);
599 if(mode.k() == 2 || mode.k() == 3) {
600 uint16_t t[4];
601 for(size_t i = 0; i < mode.k(); ++i) {
602 for(size_t j = 0; j < KyberConstants::N / 4; ++j) {
603 for(size_t k = 0; k < 4; ++k) {
604 t[k] = (((static_cast<uint32_t>(pv[i][4 * j + k]) << 10) + KyberConstants::Q / 2) /
606 0x3ff;
607 }
608
609 auto r = bs.next<5>();
610 r[0] = static_cast<uint8_t>(t[0] >> 0);
611 r[1] = static_cast<uint8_t>((t[0] >> 8) | (t[1] << 2));
612 r[2] = static_cast<uint8_t>((t[1] >> 6) | (t[2] << 4));
613 r[3] = static_cast<uint8_t>((t[2] >> 4) | (t[3] << 6));
614 r[4] = static_cast<uint8_t>(t[3] >> 2);
615 }
616 }
617 } else {
618 uint16_t t[8];
619 for(size_t i = 0; i < mode.k(); ++i) {
620 for(size_t j = 0; j < KyberConstants::N / 8; ++j) {
621 for(size_t k = 0; k < 8; ++k) {
622 t[k] = (((static_cast<uint32_t>(pv[i][8 * j + k]) << 11) + KyberConstants::Q / 2) /
624 0x7ff;
625 }
626
627 auto r = bs.next<11>();
628 r[0] = static_cast<uint8_t>(t[0] >> 0);
629 r[1] = static_cast<uint8_t>((t[0] >> 8) | (t[1] << 3));
630 r[2] = static_cast<uint8_t>((t[1] >> 5) | (t[2] << 6));
631 r[3] = static_cast<uint8_t>(t[2] >> 2);
632 r[4] = static_cast<uint8_t>((t[2] >> 10) | (t[3] << 1));
633 r[5] = static_cast<uint8_t>((t[3] >> 7) | (t[4] << 4));
634 r[6] = static_cast<uint8_t>((t[4] >> 4) | (t[5] << 7));
635 r[7] = static_cast<uint8_t>(t[5] >> 1);
636 r[8] = static_cast<uint8_t>((t[5] >> 9) | (t[6] << 2));
637 r[9] = static_cast<uint8_t>((t[6] >> 6) | (t[7] << 5));
638 r[10] = static_cast<uint8_t>(t[7] >> 3);
639 }
640 }
641 }
642
643 BOTAN_ASSERT_NOMSG(bs.full());
644 }
645
646 static void compress(std::span<uint8_t> out, Polynomial& p, const KyberConstants& mode) {
647 p.csubq();
648
649 BufferStuffer bs(out);
650 uint8_t t[8];
651 if(mode.k() == 2 || mode.k() == 3) {
652 for(size_t i = 0; i < p.size() / 8; ++i) {
653 for(size_t j = 0; j < 8; ++j) {
654 t[j] =
655 detail::ct_int_div_kyber_q((static_cast<uint16_t>(p[8 * i + j]) << 4) + KyberConstants::Q / 2) &
656 15;
657 }
658
659 auto r = bs.next<4>();
660 r[0] = t[0] | (t[1] << 4);
661 r[1] = t[2] | (t[3] << 4);
662 r[2] = t[4] | (t[5] << 4);
663 r[3] = t[6] | (t[7] << 4);
664 }
665 } else if(mode.k() == 4) {
666 for(size_t i = 0; i < p.size() / 8; ++i) {
667 for(size_t j = 0; j < 8; ++j) {
668 t[j] =
669 detail::ct_int_div_kyber_q((static_cast<uint32_t>(p[8 * i + j]) << 5) + KyberConstants::Q / 2) &
670 31;
671 }
672
673 auto r = bs.next<5>();
674 r[0] = (t[0] >> 0) | (t[1] << 5);
675 r[1] = (t[1] >> 3) | (t[2] << 2) | (t[3] << 7);
676 r[2] = (t[3] >> 1) | (t[4] << 4);
677 r[3] = (t[4] >> 4) | (t[5] << 1) | (t[6] << 6);
678 r[4] = (t[6] >> 2) | (t[7] << 3);
679 }
680 }
681
682 BOTAN_ASSERT_NOMSG(bs.full());
683 }
684
685 static PolynomialVector decompress_polynomial_vector(std::span<const uint8_t> buffer,
686 const KyberConstants& mode) {
687 BOTAN_ASSERT(buffer.size() == mode.polynomial_vector_compressed_bytes(),
688 "unexpected length of compressed polynomial vector");
689
690 PolynomialVector r(mode.k());
691 BufferSlicer bs(buffer);
692 if(mode.k() == 4) {
693 uint16_t t[8];
694 for(size_t i = 0; i < mode.k(); ++i) {
695 for(size_t j = 0; j < KyberConstants::N / 8; ++j) {
696 const auto a = bs.take<11>();
697 t[0] = (a[0] >> 0) | (static_cast<uint16_t>(a[1]) << 8);
698 t[1] = (a[1] >> 3) | (static_cast<uint16_t>(a[2]) << 5);
699 t[2] = (a[2] >> 6) | (static_cast<uint16_t>(a[3]) << 2) | (static_cast<uint16_t>(a[4]) << 10);
700 t[3] = (a[4] >> 1) | (static_cast<uint16_t>(a[5]) << 7);
701 t[4] = (a[5] >> 4) | (static_cast<uint16_t>(a[6]) << 4);
702 t[5] = (a[6] >> 7) | (static_cast<uint16_t>(a[7]) << 1) | (static_cast<uint16_t>(a[8]) << 9);
703 t[6] = (a[8] >> 2) | (static_cast<uint16_t>(a[9]) << 6);
704 t[7] = (a[9] >> 5) | (static_cast<uint16_t>(a[10]) << 3);
705
706 for(size_t k = 0; k < 8; ++k) {
707 r[i][8 * j + k] = (static_cast<uint32_t>(t[k] & 0x7FF) * KyberConstants::Q + 1024) >> 11;
708 }
709 }
710 }
711 } else {
712 uint16_t t[4];
713 for(size_t i = 0; i < mode.k(); ++i) {
714 for(size_t j = 0; j < KyberConstants::N / 4; ++j) {
715 const auto a = bs.take<5>();
716 t[0] = (a[0] >> 0) | (static_cast<uint16_t>(a[1]) << 8);
717 t[1] = (a[1] >> 2) | (static_cast<uint16_t>(a[2]) << 6);
718 t[2] = (a[2] >> 4) | (static_cast<uint16_t>(a[3]) << 4);
719 t[3] = (a[3] >> 6) | (static_cast<uint16_t>(a[4]) << 2);
720
721 for(size_t k = 0; k < 4; ++k) {
722 r[i][4 * j + k] = (static_cast<uint32_t>(t[k] & 0x3FF) * KyberConstants::Q + 512) >> 10;
723 }
724 }
725 }
726 }
727 BOTAN_ASSERT_NOMSG(bs.empty());
728
729 return r;
730 }
731
732 static Polynomial decompress_polynomial(std::span<const uint8_t> buffer, const KyberConstants& mode) {
733 BOTAN_ASSERT(buffer.size() == mode.polynomial_compressed_bytes(),
734 "unexpected length of compressed polynomial");
735
736 Polynomial r;
737 BufferSlicer bs(buffer);
738 if(mode.k() == 4) {
739 uint8_t t[8];
740 for(size_t i = 0; i < KyberConstants::N / 8; ++i) {
741 const auto a = bs.take<5>();
742 t[0] = (a[0] >> 0);
743 t[1] = (a[0] >> 5) | (a[1] << 3);
744 t[2] = (a[1] >> 2);
745 t[3] = (a[1] >> 7) | (a[2] << 1);
746 t[4] = (a[2] >> 4) | (a[3] << 4);
747 t[5] = (a[3] >> 1);
748 t[6] = (a[3] >> 6) | (a[4] << 2);
749 t[7] = (a[4] >> 3);
750
751 for(size_t j = 0; j < 8; ++j) {
752 r[8 * i + j] = (static_cast<uint32_t>(t[j] & 31) * KyberConstants::Q + 16) >> 5;
753 }
754 }
755 } else {
756 for(size_t i = 0; i < KyberConstants::N / 2; ++i) {
757 const auto a = bs.take_byte();
758 r[2 * i + 0] = ((static_cast<uint16_t>(a & 15) * KyberConstants::Q) + 8) >> 4;
759 r[2 * i + 1] = ((static_cast<uint16_t>(a >> 4) * KyberConstants::Q) + 8) >> 4;
760 }
761 }
762 BOTAN_ASSERT_NOMSG(bs.empty());
763
764 return r;
765 }
766
767 private:
768 KyberConstants m_mode;
769 PolynomialVector m_b;
770 Polynomial m_v;
771};
772
773} // namespace Botan
774
775#endif
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_DEBUG_ASSERT(expr)
Definition assert.h:98
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
bool empty() const
Definition stl_util.h:129
std::span< const uint8_t > take(const size_t count)
Definition stl_util.h:98
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:142
constexpr std::span< uint8_t > next(size_t bytes)
Definition stl_util.h:150
constexpr bool full() const
Definition stl_util.h:187
static constexpr Mask< T > is_zero(T x)
Definition ct_utils.h:245
void to_bytes(StrongSpan< KyberCompressedCiphertext > out)
KyberCompressedCiphertext to_bytes()
static Ciphertext from_bytes(StrongSpan< const KyberCompressedCiphertext > buffer, const KyberConstants &mode)
PolynomialVector & b()
Ciphertext(PolynomialVector b, const Polynomial &v, KyberConstants mode)
size_t polynomial_vector_compressed_bytes() const
size_t polynomial_compressed_bytes() const
static constexpr int16_t zetas_inv[128]
static constexpr size_t Q_Inv
static constexpr size_t kSerializedPolynomialByteLength
static constexpr int16_t zetas[128]
size_t encapsulated_key_length() const
static constexpr size_t N
size_t polynomial_vector_byte_length() const
static constexpr size_t kSymBytes
Kyber_Symmetric_Primitives & symmetric_primitives() const
static constexpr size_t Q
std::unique_ptr< Botan::XOF > XOF(StrongSpan< const KyberSeedRho > seed, std::tuple< uint8_t, uint8_t > matrix_position) const
KyberSamplingRandomness PRF(KyberSigmaOrEncryptionRandomness seed, const uint8_t nonce, const size_t outlen) const
PolynomialVector pointwise_acc_montgomery(const PolynomialVector &vec, const bool with_mont=false) const
static PolynomialMatrix generate(StrongSpan< const KyberSeedRho > seed, const bool transposed, const KyberConstants &mode)
static PolynomialVector from_bytes(std::span< const uint8_t > a, const KyberConstants &mode)
PolynomialVector(const size_t k)
Polynomial & operator[](size_t idx)
static PolynomialVector getnoise_eta1(KyberSigmaOrEncryptionRandomness seed, uint8_t nonce, const KyberConstants &mode)
PolynomialVector & operator+=(const PolynomialVector &other)
static Polynomial pointwise_acc_montgomery(const PolynomialVector &a, const PolynomialVector &b)
static PolynomialVector getnoise_eta2(StrongSpan< const KyberEncryptionRandomness > seed, uint8_t nonce, const KyberConstants &mode)
static Polynomial from_bytes(std::span< const uint8_t > a)
Polynomial & operator+=(const Polynomial &other)
int16_t & operator[](size_t idx)
static Polynomial cbd2(StrongSpan< const KyberSamplingRandomness > buf)
static Polynomial getnoise_eta2(StrongSpan< const KyberEncryptionRandomness > seed, uint8_t nonce, const KyberConstants &mode)
Polynomial & operator-=(const Polynomial &other)
static Polynomial basemul_montgomery(const Polynomial &a, const Polynomial &b)
int16_t operator[](size_t idx) const
static Polynomial cbd3(StrongSpan< const KyberSamplingRandomness > buf)
static Polynomial getnoise_eta1(KyberSigmaOrEncryptionRandomness seed, uint8_t nonce, const KyberConstants &mode)
size_t size() const
static Polynomial sample_rej_uniform(std::unique_ptr< XOF > xof)
static Polynomial from_message(StrongSpan< const KyberMessage > msg)
void to_bytes(std::span< uint8_t > out)
KyberMessage to_message()
decltype(auto) size() const noexcept(noexcept(this->m_span.size()))
FE_25519 T
Definition ge.cpp:34
constexpr uint16_t ct_int_div_kyber_q(uint32_t a)
constexpr uint32_t make_uint32(uint8_t i0, uint8_t i1, uint8_t i2, uint8_t i3)
Definition loadstor.h:100
std::variant< StrongSpan< const KyberSeedSigma >, StrongSpan< const KyberEncryptionRandomness > > KyberSigmaOrEncryptionRandomness
Definition kyber_types.h:61
constexpr auto load_le(ParamTs &&... params)
Definition loadstor.h:458