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);
83 return polyvec.size() /
N;
88 requires(std::same_as<T, U> || std::same_as<const T, U>)
89 static constexpr std::span<U, N>
poly_in_polyvec(std::span<U> polyvec,
size_t index) {
92 auto polyspan = polyvec.subspan(index *
N,
N);
93 return std::span<U, N>{polyspan.data(), polyspan.size()};
96 static constexpr T fqmul(
T a,
T b) {
return DerivedT::montgomery_reduce_coefficient(
static_cast<T2>(a) * b); }
99 static constexpr void poly_add(std::span<T, N> result, std::span<const T, N> lhs, std::span<const T, N> rhs) {
100 for(
size_t i = 0; i <
N; ++i) {
101 result[i] = lhs[i] + rhs[i];
105 static constexpr void poly_sub(std::span<T, N> result, std::span<const T, N> lhs, std::span<const T, N> rhs) {
106 for(
size_t i = 0; i <
N; ++i) {
107 result[i] = lhs[i] - rhs[i];
113 for(
auto& coeff : coeffs) {
114 using unsigned_T = std::make_unsigned_t<T>;
116 coeff += is_negative.if_set_return(
Q);
123 for(
auto& coeff : poly) {
124 coeff = DerivedT::barrett_reduce_coefficient(coeff);
130 std::span<const T> u,
131 std::span<const T> v) {
133 std::array<T, N> t{};
144 std::signed_integral<typename T::T> &&
sizeof(
typename T::T) <= 4 && std::integral<
decltype(T::N)> &&
146 requires(std::span<typename T::T, T::N> polyspan, std::span<typename T::T> polyvecspan,
typename T::T coeff) {
147 { T::to_montgomery(coeff) };
148 { T::barrett_reduce(polyspan) };
149 { T::poly_cadd_q(polyspan) };
150 { T::ntt(polyspan) };
151 { T::inverse_ntt(polyspan) };
152 { T::poly_pointwise_montgomery(polyspan, polyspan, polyspan) };
153 { T::polyvec_pointwise_acc_montgomery(polyspan, polyvecspan, polyvecspan) };
161template <Domain To,
template <
typename, Domain>
class StructureT,
crystals_trait Trait,
Domain From>
167 return StructureT<Trait, To>::from_domain_cast(std::move(p));
176template <std::
integral T,
size_t N = std::dynamic_extent>
177constexpr static bool ct_all_within_range(std::span<const T, N> range, T min, T max)
178 requires(
sizeof(T) <= 4)
182 using unsigned_T = std::make_unsigned_t<T>;
183 auto map = [](T v) -> unsigned_T {
184 if constexpr(std::signed_integral<T>) {
185 constexpr int64_t offset = -
static_cast<int64_t
>(std::numeric_limits<T>::min());
186 return static_cast<unsigned_T
>(
static_cast<int64_t
>(v) + offset);
192 const auto umin = map(min);
193 const auto umax = map(max);
196 for(
const T c : range) {
199 return mask.as_bool();
212template <crystals_trait Trait, Domain D = Domain::Normal>
216 using T =
typename Trait::T;
220 std::vector<T> m_coeffs_storage;
221 std::span<T, Trait::N> m_coeffs;
224 template <crystals_trait OtherTrait, Domain OtherD>
227 template <Domain To,
template <
typename, Domain>
class StructureT,
crystals_trait C,
Domain From>
235 template <Domain OtherD>
236 requires(D != OtherD)
238 m_coeffs_storage(std::move(other.m_coeffs_storage)),
239 m_coeffs(
owns_storage() ? std::span<T, Trait::N>(m_coeffs_storage) : other.m_coeffs) {}
247 template <Domain OtherD>
248 requires(D != OtherD)
261 m_coeffs_storage(std::move(other.m_coeffs_storage)), m_coeffs(other.m_coeffs) {}
263 ThisPolynomial&
operator=(
const ThisPolynomial& other) =
delete;
265 ThisPolynomial&
operator=(ThisPolynomial&& other)
noexcept {
268 m_coeffs_storage = std::move(other.m_coeffs_storage);
269 m_coeffs = std::span<T, Trait::N>(m_coeffs_storage);
276 constexpr size_t size()
const {
return m_coeffs.size(); }
282 copy_mem(res.m_coeffs_storage, m_coeffs);
283 res.m_coeffs = std::span<T, Trait::N>(res.m_coeffs_storage);
290 return detail::ct_all_within_range(
coefficients(), min, max);
296 for(
const auto c : m_coeffs) {
310 decltype(
auto)
begin() {
return m_coeffs.begin(); }
312 decltype(
auto)
begin()
const {
return m_coeffs.begin(); }
314 decltype(
auto)
end() {
return m_coeffs.end(); }
316 decltype(
auto)
end()
const {
return m_coeffs.end(); }
318 constexpr bool owns_storage()
const {
return !m_coeffs_storage.empty(); }
321 Trait::barrett_reduce(m_coeffs);
326 Trait::poly_cadd_q(m_coeffs);
338 decltype(
auto)
operator+=(
const ThisPolynomial& other) {
339 Trait::poly_add(m_coeffs, m_coeffs, other.m_coeffs);
347 decltype(
auto)
operator-=(
const ThisPolynomial& other) {
348 Trait::poly_sub(m_coeffs, m_coeffs, other.m_coeffs);
353template <crystals_trait Trait, Domain D = Domain::Normal>
357 using T =
typename Trait::T;
360 std::vector<T> m_polys_storage;
361 std::vector<Polynomial<Trait, D>> m_vec;
364 template <crystals_trait OtherTrait, Domain OtherD>
367 template <Domain To,
template <
typename, Domain>
class StructureT,
crystals_trait C,
Domain From>
375 template <Domain OtherD>
376 requires(D != OtherD)
379 m_polys_storage(std::move(other.m_polys_storage)) {
381 const size_t vecsize = m_polys_storage.size() / Trait::N;
382 for(
size_t i = 0; i < vecsize; ++i) {
384 Polynomial<Trait, D>(std::span{m_polys_storage}.subspan(i * Trait::N).
template first<Trait::N>()));
394 template <Domain OtherD>
395 requires(D != OtherD)
402 for(
size_t i = 0; i < vecsize; ++i) {
404 Polynomial<Trait, D>(std::span{m_polys_storage}.subspan(i * Trait::N).
template first<Trait::N>()));
410 ThisPolynomialVector&
operator=(
const ThisPolynomialVector& other) =
delete;
411 ThisPolynomialVector&
operator=(ThisPolynomialVector&& other)
noexcept =
default;
414 size_t size()
const {
return m_vec.size(); }
418 ThisPolynomialVector
clone()
const {
419 ThisPolynomialVector res(
size());
424 copy_mem(res.m_polys_storage, m_polys_storage);
432 for(
const auto c : m_polys_storage) {
440 return detail::ct_all_within_range(
coefficients(), min, max);
447 ThisPolynomialVector&
operator+=(
const ThisPolynomialVector& other) {
448 BOTAN_ASSERT(m_vec.size() == other.m_vec.size(),
"cannot add polynomial vectors of differing lengths");
449 for(
size_t i = 0; i < m_vec.size(); ++i) {
455 ThisPolynomialVector&
operator-=(
const ThisPolynomialVector& other) {
456 BOTAN_ASSERT(m_vec.size() == other.m_vec.size(),
"cannot subtract polynomial vectors of differing lengths");
457 for(
size_t i = 0; i < m_vec.size(); ++i) {
464 for(
auto& p : m_vec) {
465 Trait::barrett_reduce(p.coefficients());
471 for(
auto& v : m_vec) {
472 Trait::poly_cadd_q(v.coefficients());
481 decltype(
auto)
begin() {
return m_vec.begin(); }
483 decltype(
auto)
begin()
const {
return m_vec.begin(); }
485 decltype(
auto)
end() {
return m_vec.end(); }
487 decltype(
auto)
end()
const {
return m_vec.end(); }
494template <crystals_trait Trait>
500 std::vector<PolynomialVector<Trait, Domain::NTT>> m_mat;
507 ThisPolynomialMatrix&
operator=(
const ThisPolynomialMatrix& other) =
delete;
508 ThisPolynomialMatrix&
operator=(ThisPolynomialMatrix&& other)
noexcept =
default;
511 size_t size()
const {
return m_mat.size(); }
515 for(
size_t i = 0; i < rows; ++i) {
516 m_mat.emplace_back(cols);
524 decltype(
auto)
begin() {
return m_mat.begin(); }
526 decltype(
auto)
begin()
const {
return m_mat.begin(); }
528 decltype(
auto)
end() {
return m_mat.end(); }
530 decltype(
auto)
end()
const {
return m_mat.end(); }
539template <crystals_trait Trait, Domain D>
542 c = Trait::to_montgomery(c);
546template <crystals_trait Trait>
551 for(
size_t i = 0; i < a.
size(); ++i) {
559template <crystals_trait Trait>
562 Trait::ntt(p_ntt.coefficients());
566template <crystals_trait Trait>
569 Trait::inverse_ntt(p.coefficients());
573template <crystals_trait Trait>
576 for(
auto& poly : polyvec_ntt) {
577 Trait::ntt(poly.coefficients());
582template <crystals_trait Trait>
585 for(
auto& poly : polyvec) {
586 Trait::inverse_ntt(poly.coefficients());
591template <crystals_trait Trait, Domain D>
597template <crystals_trait Trait, Domain D>
599 for(
auto& p : polyvec) {
605template <crystals_trait Trait>
610 for(
size_t i = 0; i < a.
size(); ++i) {
611 Trait::poly_add(result[i].coefficients(), a[i].coefficients(), b[i].coefficients());
616template <crystals_trait Trait>
620 for(
size_t i = 0; i < mat.
size(); ++i) {
621 Trait::polyvec_pointwise_acc_montgomery(result[i].coefficients(), mat[i].coefficients(), vec.
coefficients());
626template <crystals_trait Trait>
634template <crystals_trait Trait>
638 for(
size_t i = 0; i < pv.
size(); ++i) {
644template <crystals_trait Trait>
652template <crystals_trait Trait>
653PolynomialVector<Trait, Domain::Normal>
operator<<(
const PolynomialVector<Trait, Domain::Normal>& pv,
size_t shift) {
655 PolynomialVector<Trait, Domain::Normal> result(pv.size());
656 for(
size_t i = 0; i < pv.size(); ++i) {
657 for(
size_t j = 0; j < Trait::N; ++j) {
658 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 poison_range(const R &r)
constexpr void unpoison_range(const R &r)
constexpr void unpoison(const T *p, size_t n)
constexpr void poison(const T *p, size_t n)
constexpr void copy_mem(T *out, const T *in, 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 clear_mem(T *ptr, size_t n)