38template <std::
signed_
integral T>
39constexpr auto is_negative_mask(T x) {
40 using unsigned_T = std::make_unsigned_t<T>;
44template <DilithiumConstants::T b>
52template <DilithiumConstants::T b>
59template <DilithiumConstants::T a, DilithiumConstants::T b, CRYSTALS::crystals_trait PolyTrait, CRYSTALS::Domain D>
61 if constexpr(a == 0) {
78 if constexpr(a == 0) {
91 (a >= 0 && b >= 0 && !
is_power_of_2(
static_cast<uint64_t
>(b) -
static_cast<uint64_t
>(a) + 1)));
94 throw Decoding_Error(
"Decoded polynomial coefficients out of range");
104 poly_pack<0, b>(p, stuffer);
115 case Gamma2::Qminus1DividedBy88:
116 return poly_pack<0, calculate_b(Gamma2::Qminus1DividedBy88)>(p, stuffer);
117 case Gamma2::Qminus1DividedBy32:
118 return poly_pack<0, calculate_b(Gamma2::Qminus1DividedBy32)>(p, stuffer);
131 case Gamma1::ToThe17th:
132 return poly_pack<Gamma1::ToThe17th - 1, Gamma1::ToThe17th>(p, stuffer);
133 case Gamma1::ToThe19th:
134 return poly_pack<Gamma1::ToThe19th - 1, Gamma1::ToThe19th>(p, stuffer);
140#if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING)
150 return poly_pack<Eta::_2, Eta::_2>(p, stuffer);
152 return poly_pack<Eta::_4, Eta::_4>(p, stuffer);
164 poly_pack<TwoToTheDminus1 - 1, TwoToTheDminus1>(p, stuffer);
178 static_assert(b >= 0 &&
is_power_of_2(
static_cast<uint32_t
>(b) + 1));
179 poly_unpack<0, b>(p, slicer);
186template <
typename ByteSourceT>
190 case Gamma1::ToThe17th:
191 return poly_unpack<Gamma1::ToThe17th - 1, Gamma1::ToThe17th>(p, byte_source);
192 case Gamma1::ToThe19th:
193 return poly_unpack<Gamma1::ToThe19th - 1, Gamma1::ToThe19th>(p, byte_source);
199#if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING)
209 return poly_unpack<Eta::_2, Eta::_2>(p, slicer, check_range);
211 return poly_unpack<Eta::_4, Eta::_4>(p, slicer, check_range);
223 poly_unpack<TwoToTheDminus1 - 1, TwoToTheDminus1>(p, slicer);
239 for(
const auto& p : h) {
240 for(
size_t i = 0; i < p.
size(); ++i) {
242 bit_positions.
append(
static_cast<uint8_t
>(i));
262 for(
auto& p : hint) {
263 const auto end_index = offsets.
take_byte();
266 if(end_index < index || end_index > mode.
omega()) {
270 const auto set_bits = bit_positions.
take(end_index - index);
274 for(
size_t i = 1; i < set_bits.size(); ++i) {
275 if(set_bits[i] <= set_bits[i - 1]) {
281 for(
const auto i : set_bits) {
289 const auto remaining = bit_positions.
take(bit_positions.
remaining());
290 if(!std::all_of(remaining.begin(), remaining.end(), [](
auto b) { return b == 0; })) {
309std::pair<DilithiumPolyVec, DilithiumPolyVec> compute_t1_and_t0(
const DilithiumPolyMatNTT& A,
312 auto t_hat = A * ntt(s1.
clone());
314 auto t = inverse_ntt(std::move(t_hat));
333 for(
const auto& p : t1) {
334 poly_pack_t1(p, stuffer);
355 poly_unpack_t1(p, slicer);
359 return {std::move(
rho), std::move(t1)};
362#if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING)
368 const auto& [pk, sk] = keypair;
371 const auto& mode = sk->mode();
377 stuffer.append(pk->rho());
378 stuffer.append(sk->signing_seed());
379 stuffer.append(pk->tr());
381 for(
const auto& p : sk->s1()) {
382 poly_pack_eta(p, stuffer, mode);
385 for(
const auto& p : sk->s2()) {
386 poly_pack_eta(p, stuffer, mode);
389 for(
const auto& p : sk->t0()) {
390 poly_pack_t0(p, stuffer);
396 return serialization;
408DilithiumInternalKeypair decode_keypair(StrongSpan<const DilithiumSerializedPrivateKey> sk, DilithiumConstants mode) {
413 BufferSlicer slicer(sk);
421 poly_unpack_eta(p, slicer, mode,
true );
426 poly_unpack_eta(p, slicer, mode,
true );
431 poly_unpack_t0(p, slicer);
443 auto [t1, _] = compute_t1_and_t0(A, s1, s2);
448 std::make_shared<Dilithium_PublicKeyInternal>(mode, std::move(
rho), std::move(t1)),
449 std::make_shared<Dilithium_PrivateKeyInternal>(std::move(mode),
459 if(keypair.first->tr() != tr) {
460 throw Decoding_Error(
"Calculated dilithium public key hash does not match the one stored in the private key");
481 for(
const auto& p : response) {
482 poly_pack_gamma1(p, stuffer, mode);
484 hint_pack(hint, stuffer, mode);
492std::optional<std::tuple<DilithiumCommitmentHash, DilithiumPolyVec, DilithiumPolyVec>>
decode_signature(
500 for(
auto& p : response) {
501 poly_unpack_gamma1(p, slicer, mode);
505 auto hint = hint_unpack(slicer, mode);
507 if(!hint.has_value()) {
511 return std::make_tuple(std::move(commitment_hash), std::move(response), std::move(hint.value()));
521 for(
const auto& p : w1) {
522 poly_pack_w1(p, stuffer, mode);
537 uint64_t signs =
load_le(bounded_xof.next<8>());
538 for(
size_t i = c.
size() - mode.
tau(); i < c.
size(); ++i) {
539 const auto j = bounded_xof.next_byte([i](uint8_t
byte) {
return byte <= i; });
541 c[j] = 1 - 2 * (signs & 1);
567 for(
auto& coeff : p) {
569 bounded_xof.next<3>([](
const auto bytes) {
return make_uint32(0, bytes[2], bytes[1], bytes[0]) & 0x7FFFFF; },
581template <DilithiumConstants::DilithiumEta eta>
582std::optional<int32_t> coeff_from_halfbyte(uint8_t b) {
587 b = b - (205 * b >> 10) * 5;
599template <DilithiumConstants::DilithiumEta eta>
605 stashed_coeff = std::optional<int32_t>{}]()
mutable -> int32_t {
606 if(
auto stashed = std::exchange(stashed_coeff, std::nullopt)) {
612 const auto b = bounded_xof.next_byte();
613 const auto z0 = coeff_from_halfbyte<eta>(b & 0x0F);
614 const auto z1 = coeff_from_halfbyte<eta>(b >> 4);
619 }
else if(z1.has_value()) {
626 for(
auto& coeff : p) {
627 coeff = next_coeff();
634void sample_uniform_eta(StrongSpan<const DilithiumSeedRhoPrime> rhoprime,
637 const DilithiumConstants& mode) {
640 auto& xof = mode.symmetric_primitives().H(rhoprime, nonce);
643 sample_uniform_eta<Eta::_2>(p, xof);
646 sample_uniform_eta<Eta::_4>(p, xof);
653 BOTAN_DEBUG_ASSERT(p.ct_validate_value_range(-
static_cast<int32_t
>(mode.eta()), mode.eta()));
668 auto [
rho, rhoprime, K] = sympriv.H(xi);
673 auto [t1, t0] = Dilithium_Algos::compute_t1_and_t0(A, s1, s2);
678 std::make_shared<Dilithium_PublicKeyInternal>(mode, std::move(
rho), std::move(t1)),
679 std::make_shared<Dilithium_PrivateKeyInternal>(
680 std::move(mode), std::move(xi), std::move(K), std::move(s1), std::move(s2), std::move(t0)),
696 for(uint8_t r = 0; r < mode.
k(); ++r) {
697 for(uint8_t s = 0; s < mode.
l(); ++s) {
698 sample_ntt_uniform(
rho, A[r][s],
load_le(std::array{s, r}), mode);
710 auto& [s1, s2] = result;
714 sample_uniform_eta(rhoprime, p, nonce++, mode);
718 sample_uniform_eta(rhoprime, p, nonce++, mode);
733 poly_unpack_gamma1(p, xof, mode);
748 const int32_t r1 = (r + (1 << (d - 1)) - 1) >> d;
749 const int32_t r0 = r - (r1 << d);
755 for(
size_t i = 0; i < vec.
size(); ++i) {
756 for(
size_t j = 0; j < vec[i].
size(); ++j) {
757 std::tie(result.first[i][j], result.second[i][j]) =
power2round(vec[i][j]);
773template <DilithiumConstants::DilithiumGamma2 gamma2>
774std::pair<int32_t, int32_t>
decompose(int32_t r) {
775 int32_t r1 = (r + 127) >> 7;
778 r1 = (r1 * 1025 + (1 << 21)) >> 22;
781 r1 = (r1 * 11275 + (1 << 23)) >> 24;
782 r1 = is_negative_mask(43 - r1).if_not_set_return(r1);
785 int32_t r0 = r - r1 * 2 * gamma2;
797template <DilithiumConstants::DilithiumGamma2 gamma2>
798std::pair<DilithiumPolyVec, DilithiumPolyVec> decompose_all_coefficients(
const DilithiumPolyVec& vec) {
801 for(
size_t i = 0; i < vec.size(); ++i) {
802 for(
size_t j = 0; j < vec[i].size(); ++j) {
803 std::tie(result.first[i][j], result.second[i][j]) =
decompose<gamma2>(vec[i][j]);
821 case Gamma2::Qminus1DividedBy32:
822 return decompose_all_coefficients<Gamma2::Qminus1DividedBy32>(vec);
824 case Gamma2::Qminus1DividedBy88:
825 return decompose_all_coefficients<Gamma2::Qminus1DividedBy88>(vec);
851 const uint32_t pc0 =
static_cast<uint32_t
>(c0);
852 const uint32_t pc1 =
static_cast<uint32_t
>(c1);
861 for(
size_t i = 0; i < r.
size(); ++i) {
862 for(
size_t j = 0; j < r[i].
size(); ++j) {
863 hint[i][j] =
static_cast<int>(
make_hint(z[i][j], r[i][j]).as_bool());
878template <DilithiumConstants::DilithiumGamma2 gamma2>
882 auto modulo_m = [](int32_t r1) -> int32_t {
883 BOTAN_DEBUG_ASSERT(r1 >= -
static_cast<decltype(r1)
>(m) && r1 <=
static_cast<decltype(r1)
>(m));
887 auto use_hint = [&modulo_m](
bool hint, int32_t r) -> int32_t {
895 return modulo_m(r1 + 1);
897 return modulo_m(r1 - 1);
901 for(
size_t i = 0; i < vec.
size(); ++i) {
902 for(
size_t j = 0; j < vec[i].
size(); ++j) {
903 vec[i][j] =
use_hint(hints[i][j], vec[i][j]);
924 case Gamma2::Qminus1DividedBy32:
925 use_hint_on_coefficients<Gamma2::Qminus1DividedBy32>(hints, vec);
927 case Gamma2::Qminus1DividedBy88:
928 use_hint_on_coefficients<Gamma2::Qminus1DividedBy88>(hints, vec);
941 for(
const auto& p : vec) {
943 const auto abs_c = c - is_negative_mask(c).if_set_return(2 * c);