Botan 3.9.0
Crypto and TLS for C&
pqcrystals_helpers.h
Go to the documentation of this file.
1/*
2 * PQ CRYSTALS Common Helpers
3 *
4 * Further changes
5 * (C) 2024 Jack Lloyd
6 * (C) 2024 René Meusel, Fabian Albert, Rohde & Schwarz Cybersecurity
7 *
8 * Botan is released under the Simplified BSD License (see license.txt)
9 */
10
11#ifndef BOTAN_PQ_CRYSTALS_HELPERS_H_
12#define BOTAN_PQ_CRYSTALS_HELPERS_H_
13
14#include <concepts>
15#include <cstdint>
16#include <span>
17#include <tuple>
18
19#include <botan/exceptn.h>
20#include <botan/internal/bit_ops.h>
21
22namespace Botan {
23
24// clang-format off
25
26template <std::unsigned_integral T>
27 requires(sizeof(T) <= 4)
29 std::conditional_t<sizeof(T) == 1, uint16_t,
30 std::conditional_t<sizeof(T) == 2, uint32_t,
31 std::conditional_t<sizeof(T) == 4, uint64_t, void>>>;
32
33template <std::signed_integral T>
34 requires(sizeof(T) <= 4)
36 std::conditional_t<sizeof(T) == 1, int16_t,
37 std::conditional_t<sizeof(T) == 2, int32_t,
38 std::conditional_t<sizeof(T) == 4, int64_t, void>>>;
39
40// clang-format on
41
42template <std::integral T>
43 requires(size_t(sizeof(T)) <= 4)
44consteval T montgomery_R(T q) {
45 using T_unsigned = std::make_unsigned_t<T>;
47 return (T2(1) << (sizeof(T) * 8)) % q;
48}
49
50template <std::integral T>
51 requires(size_t(sizeof(T)) <= 4)
52consteval T montgomery_R2(T q) {
53 using T2 = next_longer_int_t<T>;
54 return (static_cast<T2>(montgomery_R(q)) * static_cast<T2>(montgomery_R(q))) % q;
55}
56
57template <std::integral T>
58struct eea_result {
59 T gcd;
60 T u;
61 T v;
62};
63
64/**
65 * Run the extended Euclidean algorithm to find the greatest common divisor of a
66 * and b and the Bézout coefficients, u and v.
67 */
68template <std::integral T>
70 if(a > b) {
71 std::swap(a, b);
72 }
73
74 T u1 = 0;
75 T v1 = 1;
76 T u2 = 1;
77 T v2 = 0;
78
79 if(a != b) {
80 while(a != 0) {
81 const T q = b / a;
82 std::tie(a, b) = std::make_tuple(static_cast<T>(b - q * a), a);
83 std::tie(u1, v1, u2, v2) = std::make_tuple(u2, v2, static_cast<T>(u1 - q * u2), static_cast<T>(v1 - q * v2));
84 }
85 }
86
87 return {.gcd = b, .u = u1, .v = v1};
88}
89
90/**
91 * Calculate the modular multiplacative inverse of q modulo m.
92 * By default, this assumes m to be 2^bitlength of T for application in a
93 * Montgomery reduction.
94 */
95template <std::integral T, std::integral T2 = next_longer_int_t<T>>
96 requires(sizeof(T) <= 4)
97consteval T modular_inverse(T q, T2 m = T2(1) << sizeof(T) * 8) {
98 return static_cast<T>(extended_euclidean_algorithm<T2>(q, m).u);
99}
100
101constexpr auto bitlen(size_t x) {
102 return ceil_log2(x + 1);
103}
104
105/**
106 * Precompute the zeta-values for the NTT. Note that the pre-computed values
107 * contain the Montgomery factor for either Kyber or Dilithium.
108 */
109template <size_t degree, std::integral T>
110consteval static auto precompute_zetas(T q, T monty, T root_of_unity) {
111 using T2 = next_longer_int_t<T>;
112
113 std::array<T, degree> result = {0};
114
115 auto bitreverse = [](size_t k) -> size_t {
116 size_t r = 0;
117 const auto l = ceil_log2(degree);
118 for(size_t i = 0; i < l; ++i) {
119 r |= ((k >> i) & 1) << (l - 1 - i);
120 }
121 return r;
122 };
123
124 auto pow = [q](T base, size_t exp) -> T2 {
125 T2 res = 1;
126 for(size_t i = 0; i < exp; ++i) {
127 res = (res * base) % q;
128 }
129 return res;
130 };
131
132 auto csubq = [q](T a) -> T { return a <= q / 2 ? a : a - q; };
133
134 for(size_t i = 0; i < result.size(); ++i) {
135 result[i] = csubq(pow(root_of_unity, bitreverse(i)) * monty % q);
136 }
137
138 return result;
139}
140
141namespace detail {
142
143/**
144 * Wraps any XOF to limit the number of bytes that can be produced to @p bound.
145 * When the bound is reached, the XOF will throw an Internal_Error.
146 */
147template <typename XofT, size_t bound>
148 requires requires(XofT xof) {
149 { xof.template output<1>() } -> std::convertible_to<std::span<const uint8_t, 1>>;
150 { xof.template output<42>() } -> std::convertible_to<std::span<const uint8_t, 42>>;
151 }
152class Bounded_XOF final {
153 private:
154 template <size_t bytes, typename MapFnT>
155 using MappedValueT = std::invoke_result_t<MapFnT, std::array<uint8_t, bytes>>;
156
157 public:
158 template <size_t bytes>
159 constexpr static auto default_transformer(std::array<uint8_t, bytes> x) {
160 return x;
161 }
162
163 template <size_t bytes, typename T>
164 constexpr static bool default_predicate(T /*v*/) {
165 return true;
166 }
167
168 public:
170 requires std::default_initializable<XofT>
171 : m_bytes_consumed(0) {}
172
173 explicit Bounded_XOF(XofT xof) : m_xof(xof), m_bytes_consumed(0) {}
174
175 /**
176 * @returns the next byte from the XOF that fulfills @p predicate.
177 */
178 template <typename PredicateFnT = decltype(default_predicate<1, uint8_t>)>
179 requires std::invocable<PredicateFnT, uint8_t>
180 constexpr auto next_byte(PredicateFnT&& predicate = default_predicate<1, uint8_t>) {
181 return next<1>([](const auto bytes) { return bytes[0]; }, std::forward<PredicateFnT>(predicate));
182 }
183
184 /**
185 * Pulls the next @p bytes from the XOF and applies @p transformer to the
186 * output. The result is returned if @p predicate is fulfilled.
187 * @returns the transformed output of the XOF that fulfills @p predicate.
188 */
189 template <size_t bytes,
190 typename MapFnT = decltype(default_transformer<bytes>),
191 typename PredicateFnT = decltype(default_predicate<bytes, MappedValueT<bytes, MapFnT>>)>
192 requires std::invocable<MapFnT, std::array<uint8_t, bytes>> &&
193 std::invocable<PredicateFnT, MappedValueT<bytes, MapFnT>>
194 constexpr auto next(const MapFnT& transformer = default_transformer<bytes>,
195 const PredicateFnT& predicate = default_predicate<bytes, MappedValueT<bytes, MapFnT>>) {
196 while(true) {
197 auto output = transformer(take<bytes>());
198 if(predicate(output)) {
199 return output;
200 }
201 }
202 }
203
204 private:
205 template <size_t bytes>
206 constexpr std::array<uint8_t, bytes> take() {
207 m_bytes_consumed += bytes;
208 if(m_bytes_consumed > bound) {
209 throw Internal_Error("XOF consumed more bytes than allowed");
210 }
211 return m_xof.template output<bytes>();
212 }
213
214 private:
215 XofT m_xof;
216 size_t m_bytes_consumed;
217};
218
219} // namespace detail
220
221class XOF;
222
223template <size_t bound>
225
226} // namespace Botan
227
228#endif
static constexpr bool default_predicate(T)
static constexpr auto default_transformer(std::array< uint8_t, bytes > x)
constexpr auto next(const MapFnT &transformer=default_transformer< bytes >, const PredicateFnT &predicate=default_predicate< bytes, MappedValueT< bytes, MapFnT > >)
constexpr auto next_byte(PredicateFnT &&predicate=default_predicate< 1, uint8_t >)
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
std::conditional_t< sizeof(T)==1, uint16_t, std::conditional_t< sizeof(T)==2, uint32_t, std::conditional_t< sizeof(T)==4, uint64_t, void > > > next_longer_uint_t
consteval T montgomery_R2(T q)
detail::Bounded_XOF< XOF &, bound > Bounded_XOF
consteval T montgomery_R(T q)
consteval eea_result< T > extended_euclidean_algorithm(T a, T b)
constexpr uint8_t ceil_log2(T x)
Definition bit_ops.h:120
constexpr auto bitlen(size_t x)