13#ifndef BOTAN_PQ_CRYSTALS_H_
14#define BOTAN_PQ_CRYSTALS_H_
21#include <botan/assert.h>
22#include <botan/mem_ops.h>
23#include <botan/internal/ct_utils.h>
24#include <botan/internal/pqcrystals_helpers.h>
32 std::signed_integral<typename T::T> && std::integral<
decltype(T::N)> && std::integral<
decltype(T::Q)> &&
33 std::integral<
decltype(T::F)> && std::unsigned_integral<
decltype(T::NTT_Degree)> &&
34 std::integral<
decltype(T::ROOT_OF_UNITY)>;
48template <crystals_constants ConstantsT,
typename DerivedT>
51 using T =
typename ConstantsT::T;
52 static constexpr T N = ConstantsT::N;
53 static constexpr T Q = ConstantsT::Q;
73 static constexpr auto zetas = precompute_zetas<ConstantsT::NTT_Degree>(
Q,
MONTY, ConstantsT::ROOT_OF_UNITY);
81 return polyvec.size() /
N;
86 requires(std::same_as<T, U> || std::same_as<const T, U>)
87 static constexpr std::span<U, N>
poly_in_polyvec(std::span<U> polyvec,
size_t index) {
90 auto polyspan = polyvec.subspan(index *
N,
N);
91 return std::span<U, N>{polyspan.data(), polyspan.size()};
94 static constexpr T fqmul(
T a,
T b) {
return DerivedT::montgomery_reduce_coefficient(
static_cast<T2>(a) *
b); }
97 static constexpr void poly_add(std::span<T, N> result, std::span<const T, N> lhs, std::span<const T, N> rhs) {
98 for(
size_t i = 0; i <
N; ++i) {
99 result[i] = lhs[i] + rhs[i];
103 static constexpr void poly_sub(std::span<T, N> result, std::span<const T, N> lhs, std::span<const T, N> rhs) {
104 for(
size_t i = 0; i <
N; ++i) {
105 result[i] = lhs[i] - rhs[i];
111 for(
auto& coeff : coeffs) {
112 using unsigned_T = std::make_unsigned_t<T>;
114 coeff += is_negative.if_set_return(
Q);
121 for(
auto& coeff : poly) {
122 coeff = DerivedT::barrett_reduce_coefficient(coeff);
128 std::span<const T> u,
129 std::span<const T> v) {
142 std::signed_integral<typename T::T> &&
sizeof(
typename T::T) <= 4 && std::integral<
decltype(T::N)> &&
144 requires(std::span<typename T::T, T::N> polyspan, std::span<typename T::T> polyvecspan,
typename T::T coeff) {
145 { T::to_montgomery(coeff) };
146 { T::barrett_reduce(polyspan) };
147 { T::poly_cadd_q(polyspan) };
148 { T::ntt(polyspan) };
149 { T::inverse_ntt(polyspan) };
150 { T::poly_pointwise_montgomery(polyspan, polyspan, polyspan) };
151 { T::polyvec_pointwise_acc_montgomery(polyspan, polyvecspan, polyvecspan) };
159template <Domain To,
template <
typename, Domain>
class StructureT,
crystals_trait Trait,
Domain From>
165 return StructureT<Trait, To>::from_domain_cast(std::move(p));
174template <std::
integral T,
size_t N = std::dynamic_extent>
175constexpr static bool ct_all_within_range(std::span<const T, N> range,
T min,
T max)
176 requires(
sizeof(
T) <= 4)
180 using unsigned_T = std::make_unsigned_t<T>;
181 auto map = [](
T v) -> unsigned_T {
182 if constexpr(std::signed_integral<T>) {
183 constexpr int64_t offset = -
static_cast<int64_t
>(std::numeric_limits<T>::min());
184 return static_cast<unsigned_T
>(
static_cast<int64_t
>(v) + offset);
190 const auto umin = map(min);
191 const auto umax = map(max);
194 for(
const T c : range) {
197 return mask.as_bool();
210template <crystals_trait Trait, Domain D = Domain::Normal>
214 using T =
typename Trait::T;
218 std::vector<T> m_coeffs_storage;
219 std::span<T, Trait::N> m_coeffs;
222 template <crystals_trait OtherTrait, Domain OtherD>
225 template <Domain To,
template <
typename, Domain>
class StructureT,
crystals_trait C,
Domain From>
233 template <Domain OtherD>
234 requires(D != OtherD)
236 m_coeffs_storage(std::move(other.m_coeffs_storage)),
237 m_coeffs(
owns_storage() ? std::span<T, Trait::N>(m_coeffs_storage) : other.m_coeffs) {}
245 template <Domain OtherD>
246 requires(D != OtherD)
259 m_coeffs_storage(std::move(other.m_coeffs_storage)), m_coeffs(other.m_coeffs) {}
266 m_coeffs_storage = std::move(other.m_coeffs_storage);
267 m_coeffs = std::span<T, Trait::N>(m_coeffs_storage);
274 constexpr size_t size()
const {
return m_coeffs.size(); }
280 copy_mem(res.m_coeffs_storage, m_coeffs);
281 res.m_coeffs = std::span<T, Trait::N>(res.m_coeffs_storage);
288 return detail::ct_all_within_range(
coefficients(), min, max);
294 for(
const auto c : m_coeffs) {
308 decltype(
auto)
begin() {
return m_coeffs.begin(); }
310 decltype(
auto)
begin()
const {
return m_coeffs.begin(); }
312 decltype(
auto)
end() {
return m_coeffs.end(); }
314 decltype(
auto)
end()
const {
return m_coeffs.end(); }
316 constexpr bool owns_storage()
const {
return !m_coeffs_storage.empty(); }
319 Trait::barrett_reduce(m_coeffs);
324 Trait::poly_cadd_q(m_coeffs);
337 Trait::poly_add(m_coeffs, m_coeffs, other.m_coeffs);
346 Trait::poly_sub(m_coeffs, m_coeffs, other.m_coeffs);
351template <crystals_trait Trait, Domain D = Domain::Normal>
355 using T =
typename Trait::T;
358 std::vector<T> m_polys_storage;
359 std::vector<Polynomial<Trait, D>> m_vec;
362 template <crystals_trait OtherTrait, Domain OtherD>
365 template <Domain To,
template <
typename, Domain>
class StructureT,
crystals_trait C,
Domain From>
373 template <Domain OtherD>
374 requires(D != OtherD)
376 m_polys_storage(std::move(other.m_polys_storage)) {
378 const size_t vecsize = m_polys_storage.size() / Trait::N;
379 for(
size_t i = 0; i < vecsize; ++i) {
381 Polynomial<Trait, D>(std::span{m_polys_storage}.subspan(i * Trait::N).
template first<Trait::N>()));
391 template <Domain OtherD>
392 requires(D != OtherD)
399 for(
size_t i = 0; i < vecsize; ++i) {
401 Polynomial<Trait, D>(std::span{m_polys_storage}.subspan(i * Trait::N).
template first<Trait::N>()));
411 size_t size()
const {
return m_vec.size(); }
421 copy_mem(res.m_polys_storage, m_polys_storage);
429 for(
const auto c : m_polys_storage) {
437 return detail::ct_all_within_range(
coefficients(), min, max);
445 BOTAN_ASSERT(m_vec.size() == other.m_vec.size(),
"cannot add polynomial vectors of differing lengths");
446 for(
size_t i = 0; i < m_vec.size(); ++i) {
453 BOTAN_ASSERT(m_vec.size() == other.m_vec.size(),
"cannot subtract polynomial vectors of differing lengths");
454 for(
size_t i = 0; i < m_vec.size(); ++i) {
461 for(
auto& p : m_vec) {
462 Trait::barrett_reduce(p.coefficients());
468 for(
auto& v : m_vec) {
469 Trait::poly_cadd_q(v.coefficients());
478 decltype(
auto)
begin() {
return m_vec.begin(); }
480 decltype(
auto)
begin()
const {
return m_vec.begin(); }
482 decltype(
auto)
end() {
return m_vec.end(); }
484 decltype(
auto)
end()
const {
return m_vec.end(); }
491template <crystals_trait Trait>
497 std::vector<PolynomialVector<Trait, Domain::NTT>> m_mat;
508 size_t size()
const {
return m_mat.size(); }
512 for(
size_t i = 0; i < rows; ++i) {
513 m_mat.emplace_back(cols);
521 decltype(
auto)
begin() {
return m_mat.begin(); }
523 decltype(
auto)
begin()
const {
return m_mat.begin(); }
525 decltype(
auto)
end() {
return m_mat.end(); }
527 decltype(
auto)
end()
const {
return m_mat.end(); }
536template <crystals_trait Trait, Domain D>
539 c = Trait::to_montgomery(c);
543template <crystals_trait Trait>
547 BOTAN_ASSERT(a.
size() ==
b.size(),
"Dot product requires equally sized PolynomialVectors");
548 for(
size_t i = 0; i < a.
size(); ++i) {
556template <crystals_trait Trait>
559 Trait::ntt(p_ntt.coefficients());
563template <crystals_trait Trait>
566 Trait::inverse_ntt(p.coefficients());
570template <crystals_trait Trait>
573 for(
auto& poly : polyvec_ntt) {
574 Trait::ntt(poly.coefficients());
579template <crystals_trait Trait>
582 for(
auto& poly : polyvec) {
583 Trait::inverse_ntt(poly.coefficients());
588template <crystals_trait Trait, Domain D>
594template <crystals_trait Trait, Domain D>
596 for(
auto& p : polyvec) {
602template <crystals_trait Trait>
607 for(
size_t i = 0; i < a.
size(); ++i) {
608 Trait::poly_add(result[i].coefficients(), a[i].coefficients(),
b[i].coefficients());
613template <crystals_trait Trait>
617 for(
size_t i = 0; i < mat.
size(); ++i) {
618 Trait::polyvec_pointwise_acc_montgomery(result[i].coefficients(), mat[i].coefficients(), vec.
coefficients());
623template <crystals_trait Trait>
631template <crystals_trait Trait>
635 for(
size_t i = 0; i < pv.
size(); ++i) {
641template <crystals_trait Trait>
649template <crystals_trait Trait>
650PolynomialVector<Trait, Domain::Normal>
operator<<(
const PolynomialVector<Trait, Domain::Normal>& pv,
size_t shift) {
652 PolynomialVector<Trait, Domain::Normal> result(pv.size());
653 for(
size_t i = 0; i < pv.size(); ++i) {
654 for(
size_t j = 0; j < Trait::N; ++j) {
655 result[i][j] = pv[i][j] << shift;
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_DEBUG_ASSERT(expr)
#define BOTAN_ASSERT(expr, assertion_made)
PolynomialMatrix(size_t rows, size_t cols)
PolynomialVector< Trait, Domain::NTT > & operator[](size_t i)
PolynomialMatrix(ThisPolynomialMatrix &&other) noexcept=default
ThisPolynomialMatrix & operator=(const ThisPolynomialMatrix &other)=delete
decltype(auto) end() const
void _const_time_poison() const
PolynomialMatrix(const ThisPolynomialMatrix &other)=delete
const PolynomialVector< Trait, Domain::NTT > & operator[](size_t i) const
ThisPolynomialMatrix & operator=(ThisPolynomialMatrix &&other) noexcept=default
PolynomialMatrix(std::vector< PolynomialVector< Trait > > mat)
~PolynomialMatrix()=default
void _const_time_unpoison() const
decltype(auto) begin() const
ThisPolynomialVector & operator=(const ThisPolynomialVector &other)=delete
void _const_time_poison() const
~PolynomialVector()=default
Polynomial< Trait, D > & operator[](size_t i)
std::span< const T > coefficients() const
ThisPolynomialVector & reduce()
constexpr Domain domain() const noexcept
ThisPolynomialVector & operator-=(const ThisPolynomialVector &other)
size_t hamming_weight() const noexcept
PolynomialVector(const ThisPolynomialVector &other)=delete
void _const_time_unpoison() const
ThisPolynomialVector & operator=(ThisPolynomialVector &&other) noexcept=default
decltype(auto) begin() const
const Polynomial< Trait, D > & operator[](size_t i) const
ThisPolynomialVector clone() const
PolynomialVector(ThisPolynomialVector &&other) noexcept=default
constexpr bool ct_validate_value_range(T min, T max) const noexcept
ThisPolynomialVector & conditional_add_q()
PolynomialVector(size_t vecsize)
decltype(auto) end() const
friend class PolynomialVector
static PolynomialVector< Trait, D > from_domain_cast(PolynomialVector< Trait, OtherD > &&other)
std::span< T > coefficients()
ThisPolynomialVector & operator+=(const ThisPolynomialVector &other)
Polynomial(const ThisPolynomial &other)=delete
constexpr bool owns_storage() const
void _const_time_unpoison() const
ThisPolynomial & operator=(ThisPolynomial &&other) noexcept
T operator[](size_t i) const
Polynomial(std::span< T, Trait::N > coeffs)
Polynomial(ThisPolynomial &&other) noexcept
ThisPolynomial & conditional_add_q()
std::span< const T, Trait::N > coefficients() const
ThisPolynomial & operator=(const ThisPolynomial &other)=delete
constexpr size_t hamming_weight() const noexcept
static Polynomial< Trait, D > from_domain_cast(Polynomial< Trait, OtherD > &&p)
decltype(auto) end() const
std::span< T, Trait::N > coefficients()
constexpr size_t size() const
void _const_time_poison() const
constexpr Domain domain() const noexcept
ThisPolynomial & reduce()
ThisPolynomial clone() const
decltype(auto) begin() const
constexpr bool ct_validate_value_range(T min, T max) const noexcept
static constexpr void barrett_reduce(std::span< T, N > poly)
static constexpr T to_montgomery(T a)
static constexpr T F_WITH_MONTY_SQUARED
static constexpr void poly_sub(std::span< T, N > result, std::span< const T, N > lhs, std::span< const T, N > rhs)
next_longer_int_t< T > T2
static constexpr T Q_inverse
static constexpr T MONTY_SQUARED
static constexpr void poly_cadd_q(std::span< T, N > coeffs)
Adds Q if the coefficient is negative.
static constexpr T fqmul(T a, T b)
static constexpr auto zetas
static constexpr void poly_add(std::span< T, N > result, std::span< const T, N > lhs, std::span< const T, N > rhs)
static constexpr size_t polys_in_polyvec(std::span< const T > polyvec)
static constexpr std::span< U, N > poly_in_polyvec(std::span< U > polyvec, size_t index)
static constexpr void polyvec_pointwise_acc_montgomery(std::span< T, N > w, std::span< const T > u, std::span< const T > v)
Multiplication and accumulation of 2 polynomial vectors u and v.
static constexpr Mask< T > set()
static constexpr Mask< T > expand_top_bit(T v)
static constexpr Mask< T > is_within_range(T v, T l, T u)
void dot_product(Polynomial< Trait, Domain::NTT > &out, const PolynomialVector< Trait, Domain::NTT > &a, const PolynomialVector< Trait, Domain::NTT > &b)
void montgomery(Polynomial< Trait, D > &p)
StructureT< Trait, To > domain_cast(StructureT< Trait, From > &&p)
Polynomial< Trait, D > montgomery(Polynomial< Trait, D > p)
PolynomialVector< Trait, Domain::NTT > operator*(const PolynomialMatrix< Trait > &mat, const PolynomialVector< Trait, Domain::NTT > &vec)
PolynomialVector< Trait, Domain::Normal > operator+(const PolynomialVector< Trait, Domain::Normal > &a, const PolynomialVector< Trait, Domain::Normal > &b)
Polynomial< Trait, Domain::NTT > ntt(Polynomial< Trait, Domain::Normal > p)
PolynomialVector< Trait, Domain::Normal > operator<<(const PolynomialVector< Trait, Domain::Normal > &pv, size_t shift)
Polynomial< Trait, Domain::Normal > inverse_ntt(Polynomial< Trait, Domain::NTT > p_ntt)
constexpr void unpoison_range(R &&r)
constexpr void poison_range(R &&r)
constexpr void unpoison(const T *p, size_t n)
constexpr void poison(const T *p, size_t n)
consteval T modular_inverse(T q, T2 m=T2(1)<< sizeof(T) *8)
std::conditional_t< sizeof(T)==1, int16_t, std::conditional_t< sizeof(T)==2, int32_t, std::conditional_t< sizeof(T)==4, int64_t, void > > > next_longer_int_t
consteval T montgomery_R2(T q)
consteval T montgomery_R(T q)
constexpr void copy_mem(T *out, const T *in, size_t n)
constexpr void clear_mem(T *ptr, size_t n)