39template <std::
signed_
integral T>
40constexpr auto is_negative_mask(
T x) {
41 using unsigned_T = std::make_unsigned_t<T>;
45template <DilithiumConstants::T b>
53template <DilithiumConstants::T b>
60template <DilithiumConstants::T a, DilithiumConstants::T b, CRYSTALS::crystals_trait PolyTrait, CRYSTALS::Domain D>
62 if constexpr(a == 0) {
79 if constexpr(a == 0) {
92 (a >= 0 &&
b >= 0 && !
is_power_of_2(
static_cast<uint64_t
>(
b) -
static_cast<uint64_t
>(a) + 1)));
95 throw Decoding_Error(
"Decoded polynomial coefficients out of range");
105 poly_pack<0, b>(p, stuffer);
116 case Gamma2::Qminus1DevidedBy88:
117 return poly_pack<0, calculate_b(Gamma2::Qminus1DevidedBy88)>(p, stuffer);
118 case Gamma2::Qminus1DevidedBy32:
119 return poly_pack<0, calculate_b(Gamma2::Qminus1DevidedBy32)>(p, stuffer);
132 case Gamma1::ToThe17th:
133 return poly_pack<Gamma1::ToThe17th - 1, Gamma1::ToThe17th>(p, stuffer);
134 case Gamma1::ToThe19th:
135 return poly_pack<Gamma1::ToThe19th - 1, Gamma1::ToThe19th>(p, stuffer);
141#if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING)
151 return poly_pack<Eta::_2, Eta::_2>(p, stuffer);
153 return poly_pack<Eta::_4, Eta::_4>(p, stuffer);
165 poly_pack<TwoToTheDminus1 - 1, TwoToTheDminus1>(p, stuffer);
180 poly_unpack<0, b>(p, slicer);
187template <
typename ByteSourceT>
191 case Gamma1::ToThe17th:
192 return poly_unpack<Gamma1::ToThe17th - 1, Gamma1::ToThe17th>(p, byte_source);
193 case Gamma1::ToThe19th:
194 return poly_unpack<Gamma1::ToThe19th - 1, Gamma1::ToThe19th>(p, byte_source);
200#if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING)
210 return poly_unpack<Eta::_2, Eta::_2>(p, slicer, check_range);
212 return poly_unpack<Eta::_4, Eta::_4>(p, slicer, check_range);
224 poly_unpack<TwoToTheDminus1 - 1, TwoToTheDminus1>(p, slicer);
240 for(
const auto& p : h) {
241 for(
size_t i = 0; i < p.
size(); ++i) {
243 bit_positions.
append(
static_cast<uint8_t
>(i));
263 for(
auto& p : hint) {
264 const auto end_index = offsets.
take_byte();
267 if(end_index < index || end_index > mode.
omega()) {
271 const auto set_bits = bit_positions.
take(end_index - index);
275 for(
size_t i = 1; i < set_bits.size(); ++i) {
276 if(set_bits[i] <= set_bits[i - 1]) {
282 for(
const auto i : set_bits) {
290 const auto remaining = bit_positions.
take(bit_positions.
remaining());
291 if(!std::all_of(remaining.begin(), remaining.end(), [](
auto b) { return b == 0; })) {
310std::pair<DilithiumPolyVec, DilithiumPolyVec> compute_t1_and_t0(
const DilithiumPolyMatNTT& A,
313 auto t_hat = A * ntt(s1.
clone());
315 auto t = inverse_ntt(std::move(t_hat));
334 for(
const auto& p : t1) {
335 poly_pack_t1(p, stuffer);
356 poly_unpack_t1(p, slicer);
360 return {std::move(
rho), std::move(t1)};
363#if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING)
369 auto& [pk, sk] = keypair;
372 const auto& mode = sk->mode();
378 stuffer.append(pk->rho());
379 stuffer.append(sk->signing_seed());
380 stuffer.append(pk->tr());
382 for(
const auto& p : sk->s1()) {
383 poly_pack_eta(p, stuffer, mode);
386 for(
const auto& p : sk->s2()) {
387 poly_pack_eta(p, stuffer, mode);
390 for(
const auto& p : sk->t0()) {
391 poly_pack_t0(p, stuffer);
397 return serialization;
409DilithiumInternalKeypair decode_keypair(StrongSpan<const DilithiumSerializedPrivateKey> sk, DilithiumConstants mode) {
414 BufferSlicer slicer(sk);
422 poly_unpack_eta(p, slicer, mode,
true );
427 poly_unpack_eta(p, slicer, mode,
true );
432 poly_unpack_t0(p, slicer);
444 auto [t1, _] = compute_t1_and_t0(A, s1, s2);
449 std::make_shared<Dilithium_PublicKeyInternal>(mode, std::move(
rho), std::move(t1)),
450 std::make_shared<Dilithium_PrivateKeyInternal>(std::move(mode),
460 if(keypair.first->tr() != tr) {
461 throw Decoding_Error(
"Calculated dilithium public key hash does not match the one stored in the private key");
482 for(
const auto& p : response) {
483 poly_pack_gamma1(p, stuffer, mode);
485 hint_pack(hint, stuffer, mode);
493std::optional<std::tuple<DilithiumCommitmentHash, DilithiumPolyVec, DilithiumPolyVec>>
decode_signature(
501 for(
auto& p : response) {
502 poly_unpack_gamma1(p, slicer, mode);
506 auto hint = hint_unpack(slicer, mode);
508 if(!hint.has_value()) {
512 return std::make_tuple(std::move(commitment_hash), std::move(response), std::move(hint.value()));
522 for(
const auto& p : w1) {
523 poly_pack_w1(p, stuffer, mode);
538 uint64_t signs =
load_le(bounded_xof.next<8>());
539 for(
size_t i = c.
size() - mode.
tau(); i < c.
size(); ++i) {
540 const auto j = bounded_xof.next_byte([i](uint8_t
byte) {
return byte <= i; });
542 c[j] = 1 - 2 * (signs & 1);
568 for(
auto& coeff : p) {
570 bounded_xof.next<3>([](
const auto bytes) {
return make_uint32(0, bytes[2], bytes[1], bytes[0]) & 0x7FFFFF; },
582template <DilithiumConstants::DilithiumEta eta>
583std::optional<int32_t> coeff_from_halfbyte(uint8_t
b) {
588 b =
b - (205 *
b >> 10) * 5;
600template <DilithiumConstants::DilithiumEta eta>
606 stashed_coeff = std::optional<int32_t>{}]()
mutable -> int32_t {
607 if(
auto stashed = std::exchange(stashed_coeff, std::nullopt)) {
613 const auto b = bounded_xof.next_byte();
614 const auto z0 = coeff_from_halfbyte<eta>(
b & 0x0F);
615 const auto z1 = coeff_from_halfbyte<eta>(
b >> 4);
620 }
else if(z1.has_value()) {
627 for(
auto& coeff : p) {
628 coeff = next_coeff();
635void sample_uniform_eta(StrongSpan<const DilithiumSeedRhoPrime> rhoprime,
638 const DilithiumConstants& mode) {
641 auto& xof = mode.symmetric_primitives().H(rhoprime, nonce);
644 sample_uniform_eta<Eta::_2>(p, xof);
647 sample_uniform_eta<Eta::_4>(p, xof);
654 BOTAN_DEBUG_ASSERT(p.ct_validate_value_range(-
static_cast<int32_t
>(mode.eta()), mode.eta()));
669 auto [
rho, rhoprime, K] = sympriv.H(xi);
674 auto [t1, t0] = Dilithium_Algos::compute_t1_and_t0(A, s1, s2);
679 std::make_shared<Dilithium_PublicKeyInternal>(mode, std::move(
rho), std::move(t1)),
680 std::make_shared<Dilithium_PrivateKeyInternal>(
681 std::move(mode), std::move(xi), std::move(K), std::move(s1), std::move(s2), std::move(t0)),
697 for(uint8_t r = 0; r < mode.
k(); ++r) {
698 for(uint8_t s = 0; s < mode.
l(); ++s) {
699 sample_ntt_uniform(
rho, A[r][s],
load_le(std::array{s, r}), mode);
711 auto& [s1, s2] = result;
715 sample_uniform_eta(rhoprime, p, nonce++, mode);
719 sample_uniform_eta(rhoprime, p, nonce++, mode);
734 poly_unpack_gamma1(p, xof, mode);
749 const int32_t r1 = (r + (1 << (d - 1)) - 1) >> d;
750 const int32_t r0 = r - (r1 << d);
756 for(
size_t i = 0; i < vec.
size(); ++i) {
757 for(
size_t j = 0; j < vec[i].
size(); ++j) {
758 std::tie(result.first[i][j], result.second[i][j]) =
power2round(vec[i][j]);
774template <DilithiumConstants::DilithiumGamma2 gamma2>
775std::pair<int32_t, int32_t>
decompose(int32_t r) {
776 int32_t r1 = (r + 127) >> 7;
779 r1 = (r1 * 1025 + (1 << 21)) >> 22;
782 r1 = (r1 * 11275 + (1 << 23)) >> 24;
783 r1 = is_negative_mask(43 - r1).if_not_set_return(r1);
786 int32_t r0 = r - r1 * 2 * gamma2;
798template <DilithiumConstants::DilithiumGamma2 gamma2>
799std::pair<DilithiumPolyVec, DilithiumPolyVec> decompose_all_coefficents(
const DilithiumPolyVec& vec) {
802 for(
size_t i = 0; i < vec.size(); ++i) {
803 for(
size_t j = 0; j < vec[i].size(); ++j) {
804 std::tie(result.first[i][j], result.second[i][j]) =
decompose<gamma2>(vec[i][j]);
822 case Gamma2::Qminus1DevidedBy32:
823 return decompose_all_coefficents<Gamma2::Qminus1DevidedBy32>(vec);
825 case Gamma2::Qminus1DevidedBy88:
826 return decompose_all_coefficents<Gamma2::Qminus1DevidedBy88>(vec);
852 const uint32_t pc0 =
static_cast<uint32_t
>(c0);
853 const uint32_t pc1 =
static_cast<uint32_t
>(c1);
862 for(
size_t i = 0; i < r.
size(); ++i) {
863 for(
size_t j = 0; j < r[i].
size(); ++j) {
864 hint[i][j] =
make_hint(z[i][j], r[i][j]).as_bool();
879template <DilithiumConstants::DilithiumGamma2 gamma2>
883 auto modulo_m = [](int32_t r1) -> int32_t {
884 BOTAN_DEBUG_ASSERT(r1 >= -
static_cast<decltype(r1)
>(m) && r1 <=
static_cast<decltype(r1)
>(m));
888 auto use_hint = [&modulo_m](
bool hint, int32_t r) -> int32_t {
896 return modulo_m(r1 + 1);
898 return modulo_m(r1 - 1);
902 for(
size_t i = 0; i < vec.
size(); ++i) {
903 for(
size_t j = 0; j < vec[i].
size(); ++j) {
904 vec[i][j] =
use_hint(hints[i][j], vec[i][j]);
925 case Gamma2::Qminus1DevidedBy32:
926 use_hint_on_coefficients<Gamma2::Qminus1DevidedBy32>(hints, vec);
928 case Gamma2::Qminus1DevidedBy88:
929 use_hint_on_coefficients<Gamma2::Qminus1DevidedBy88>(hints, vec);
942 for(
const auto& p : vec) {
944 const auto abs_c = c - is_negative_mask(c).if_set_return(2 * c);