Coverage Report

Created: 2025-04-11 06:34

/src/botan/build/include/internal/botan/internal/pqcrystals_helpers.h
Line
Count
Source (jump to first uncovered line)
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
22
namespace Botan {
23
24
// clang-format off
25
26
template <std::unsigned_integral T>
27
   requires(sizeof(T) <= 4)
28
using next_longer_uint_t =
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
33
template <std::signed_integral T>
34
   requires(sizeof(T) <= 4)
35
using next_longer_int_t =
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
42
template <std::integral T>
43
   requires(size_t(sizeof(T)) <= 4)
44
consteval T montgomery_R(T q) {
45
   using T_unsigned = std::make_unsigned_t<T>;
46
   using T2 = next_longer_uint_t<T_unsigned>;
47
   return (T2(1) << (sizeof(T) * 8)) % q;
48
}
49
50
template <std::integral T>
51
   requires(size_t(sizeof(T)) <= 4)
52
consteval 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
57
template <std::integral T>
58
struct 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
 */
68
template <std::integral T>
69
consteval eea_result<T> extended_euclidean_algorithm(T a, T b) {
70
   if(a > b) {
71
      std::swap(a, b);
72
   }
73
74
   T u1 = 0, v1 = 1, u2 = 1, v2 = 0;
75
76
   if(a != b) {
77
      while(a != 0) {
78
         const T q = b / a;
79
         std::tie(a, b) = std::make_tuple(static_cast<T>(b - q * a), a);
80
         std::tie(u1, v1, u2, v2) = std::make_tuple(u2, v2, static_cast<T>(u1 - q * u2), static_cast<T>(v1 - q * v2));
81
      }
82
   }
83
84
   return {.gcd = b, .u = u1, .v = v1};
85
}
86
87
/**
88
 * Calculate the modular multiplacative inverse of q modulo m.
89
 * By default, this assumes m to be 2^bitlength of T for application in a
90
 * Montgomery reduction.
91
 */
92
template <std::integral T, std::integral T2 = next_longer_int_t<T>>
93
   requires(sizeof(T) <= 4)
94
consteval T modular_inverse(T q, T2 m = T2(1) << sizeof(T) * 8) {
95
   return static_cast<T>(extended_euclidean_algorithm<T2>(q, m).u);
96
}
97
98
106
constexpr auto bitlen(size_t x) {
99
106
   return ceil_log2(x + 1);
100
106
}
101
102
/**
103
 * Precompute the zeta-values for the NTT. Note that the pre-computed values
104
 * contain the Montgomery factor for either Kyber or Dilithium.
105
 */
106
template <size_t degree, std::integral T>
107
consteval static auto precompute_zetas(T q, T monty, T root_of_unity) {
108
   using T2 = next_longer_int_t<T>;
109
110
   std::array<T, degree> result = {0};
111
112
   auto bitreverse = [](size_t k) -> size_t {
113
      size_t r = 0;
114
      const auto l = ceil_log2(degree);
115
      for(size_t i = 0; i < l; ++i) {
116
         r |= ((k >> i) & 1) << (l - 1 - i);
117
      }
118
      return r;
119
   };
120
121
   auto pow = [q](T base, size_t exp) -> T2 {
122
      T2 res = 1;
123
      for(size_t i = 0; i < exp; ++i) {
124
         res = (res * base) % q;
125
      }
126
      return res;
127
   };
128
129
   auto csubq = [q](T a) -> T { return a <= q / 2 ? a : a - q; };
130
131
   for(size_t i = 0; i < result.size(); ++i) {
132
      result[i] = csubq(pow(root_of_unity, bitreverse(i)) * monty % q);
133
   }
134
135
   return result;
136
}
137
138
namespace detail {
139
140
/**
141
 * Wraps any XOF to limit the number of bytes that can be produced to @p bound.
142
 * When the bound is reached, the XOF will throw an Internal_Error.
143
 */
144
template <typename XofT, size_t bound>
145
   requires requires(XofT xof) {
146
      { xof.template output<1>() } -> std::convertible_to<std::span<const uint8_t, 1>>;
147
      { xof.template output<42>() } -> std::convertible_to<std::span<const uint8_t, 42>>;
148
   }
149
class Bounded_XOF final {
150
   private:
151
      template <size_t bytes, typename MapFnT>
152
      using MappedValueT = std::invoke_result_t<MapFnT, std::array<uint8_t, bytes>>;
153
154
   public:
155
      template <size_t bytes>
156
0
      constexpr static auto default_transformer(std::array<uint8_t, bytes> x) {
157
0
         return x;
158
0
      }
159
160
      template <size_t bytes, typename T>
161
0
      constexpr static bool default_predicate(T) {
162
0
         return true;
163
0
      }
Unexecuted instantiation: bool Botan::detail::Bounded_XOF<Botan::XOF&, 229ul>::default_predicate<8ul, std::__1::array<unsigned char, 8ul> >(std::__1::array<unsigned char, 8ul>)
Unexecuted instantiation: bool Botan::detail::Bounded_XOF<Botan::XOF&, 481ul>::default_predicate<1ul, unsigned char>(unsigned char)
Unexecuted instantiation: bool Botan::detail::Bounded_XOF<Botan::XOF&, 229ul>::default_predicate<1ul, unsigned char>(unsigned char)
Unexecuted instantiation: bool Botan::detail::Bounded_XOF<Botan::XOF&, 894ul>::default_predicate<1ul, unsigned char>(unsigned char)
Unexecuted instantiation: bool Botan::detail::Bounded_XOF<Botan::XOF&, 840ul>::default_predicate<3ul, std::__1::pair<std::__1::optional<unsigned short>, std::__1::optional<unsigned short> > >(std::__1::pair<std::__1::optional<unsigned short>, std::__1::optional<unsigned short> >)
Unexecuted instantiation: bool Botan::detail::Bounded_XOF<Botan::XOF&, 840ul>::default_predicate<1ul, unsigned char>(unsigned char)
164
165
   public:
166
      Bounded_XOF()
167
         requires std::default_initializable<XofT>
168
            : m_bytes_consumed(0) {}
169
170
0
      explicit Bounded_XOF(XofT xof) : m_xof(xof), m_bytes_consumed(0) {}
Unexecuted instantiation: Botan::detail::Bounded_XOF<Botan::XOF&, 894ul>::Bounded_XOF(Botan::XOF&)
Unexecuted instantiation: Botan::detail::Bounded_XOF<Botan::XOF&, 481ul>::Bounded_XOF(Botan::XOF&)
Unexecuted instantiation: Botan::detail::Bounded_XOF<Botan::XOF&, 229ul>::Bounded_XOF(Botan::XOF&)
Unexecuted instantiation: Botan::detail::Bounded_XOF<Botan::XOF&, 840ul>::Bounded_XOF(Botan::XOF&)
171
172
      /**
173
       * @returns the next byte from the XOF that fulfills @p predicate.
174
       */
175
      template <typename PredicateFnT = decltype(default_predicate<1, uint8_t>)>
176
         requires std::invocable<PredicateFnT, uint8_t>
177
0
      constexpr auto next_byte(PredicateFnT&& predicate = default_predicate<1, uint8_t>) {
178
0
         return next<1>([](const auto bytes) { return bytes[0]; }, std::forward<PredicateFnT>(predicate));
Unexecuted instantiation: dilithium_algos.cpp:_ZZN5Botan6detail11Bounded_XOFIRNS_3XOFELm229EE9next_byteIZNS_15Dilithium_Algos14sample_in_ballENS_10StrongSpanIKNS_6StrongINSt3__16vectorIhNS9_9allocatorIhEEEENS_24DilithiumCommitmentHash_EJEEEEERKNS_18DilithiumConstantsEE3$_0Qsr3stdE9invocableITL0__hEEEDaOT_ENKUlSN_E_clINS9_5arrayIhLm1EEEEEDaSN_
Unexecuted instantiation: _ZZN5Botan6detail11Bounded_XOFIRNS_3XOFELm481EE9next_byteIFbhEQsr3stdE9invocableITL0__hEEEDaOT_ENKUlS8_E_clINSt3__15arrayIhLm1EEEEEDaS8_
179
0
      }
Unexecuted instantiation: dilithium_algos.cpp:_ZN5Botan6detail11Bounded_XOFIRNS_3XOFELm229EE9next_byteIZNS_15Dilithium_Algos14sample_in_ballENS_10StrongSpanIKNS_6StrongINSt3__16vectorIhNS9_9allocatorIhEEEENS_24DilithiumCommitmentHash_EJEEEEERKNS_18DilithiumConstantsEE3$_0Qsr3stdE9invocableITL0__hEEEDaOT_
Unexecuted instantiation: _ZN5Botan6detail11Bounded_XOFIRNS_3XOFELm481EE9next_byteIFbhEQsr3stdE9invocableITL0__hEEEDaOT_
180
181
      /**
182
       * Pulls the next @p bytes from the XOF and applies @p transformer to the
183
       * output. The result is returned if @p predicate is fulfilled.
184
       * @returns the transformed output of the XOF that fulfills @p predicate.
185
       */
186
      template <size_t bytes,
187
                typename MapFnT = decltype(default_transformer<bytes>),
188
                typename PredicateFnT = decltype(default_predicate<bytes, MappedValueT<bytes, MapFnT>>)>
189
         requires std::invocable<MapFnT, std::array<uint8_t, bytes>> &&
190
                  std::invocable<PredicateFnT, MappedValueT<bytes, MapFnT>>
191
      constexpr auto next(MapFnT&& transformer = default_transformer<bytes>,
192
0
                          PredicateFnT&& predicate = default_predicate<bytes, MappedValueT<bytes, MapFnT>>) {
193
0
         while(true) {
194
0
            auto output = transformer(take<bytes>());
195
0
            if(predicate(output)) {
196
0
               return output;
197
0
            }
198
0
         }
199
0
      }
Unexecuted instantiation: _ZN5Botan6detail11Bounded_XOFIRNS_3XOFELm229EE4nextILm8EFNSt3__15arrayIhLm8EEES8_EFbS8_EQaasr3stdE9invocableITL0_0_NS7_IhXTL0__EEEEsr3stdE9invocableITL0_1_NS6_13invoke_resultISB_JSC_EE4typeEEEEDaOT0_OT1_
Unexecuted instantiation: dilithium_algos.cpp:_ZN5Botan6detail11Bounded_XOFIRNS_3XOFELm229EE4nextILm1EZNS4_9next_byteIZNS_15Dilithium_Algos14sample_in_ballENS_10StrongSpanIKNS_6StrongINSt3__16vectorIhNSA_9allocatorIhEEEENS_24DilithiumCommitmentHash_EJEEEEERKNS_18DilithiumConstantsEE3$_0Qsr3stdE9invocableITL0__hEEEDaOT_EUlSO_E_SM_Qaasr3stdE9invocableITL0_0_NSA_5arrayIhXTL0__EEEEsr3stdE9invocableITL0_1_NSA_13invoke_resultISR_JST_EE4typeEEEEDaOT0_OT1_
Unexecuted instantiation: dilithium_algos.cpp:_ZN5Botan6detail11Bounded_XOFIRNS_3XOFELm894EE4nextILm3EZNS_15Dilithium_Algos12_GLOBAL__N_118sample_ntt_uniformENS_10StrongSpanIKNS_6StrongINSt3__16vectorIhNSA_9allocatorIhEEEENS_20DilithiumPublicSeed_EJEEEEERNS_8CRYSTALS10PolynomialINS_19DilithiumPolyTraitsELNSJ_6DomainE1EEEtRKNS_18DilithiumConstantsEE3$_0ZNS7_18sample_ntt_uniformESI_SO_tSR_E3$_1Qaasr3stdE9invocableITL0_0_NSA_5arrayIhXTL0__EEEEsr3stdE9invocableITL0_1_NSA_13invoke_resultISU_JSW_EE4typeEEEEDaOT0_OT1_
Unexecuted instantiation: _ZN5Botan6detail11Bounded_XOFIRNS_3XOFELm481EE4nextILm1EZNS4_9next_byteIFbhEQsr3stdE9invocableITL0__hEEEDaOT_EUlS9_E_RS7_Qaasr3stdE9invocableITL0_0_NSt3__15arrayIhXTL0__EEEEsr3stdE9invocableITL0_1_NSE_13invoke_resultISD_JSG_EE4typeEEEEDaOT0_OT1_
Unexecuted instantiation: kyber_algos.cpp:_ZN5Botan6detail11Bounded_XOFIRNS_3XOFELm840EE4nextILm3EZZNS_11Kyber_Algos12_GLOBAL__N_118sample_ntt_uniformERNS_8CRYSTALS10PolynomialINS_15KyberPolyTraitsELNS8_6DomainE1EEES3_EN3$_0clEvEUlT_E_FbNSt3__14pairINSH_8optionalItEESK_EEEQaasr3stdE9invocableITL0_0_NSH_5arrayIhXTL0__EEEEsr3stdE9invocableITL0_1_NSH_13invoke_resultISN_JSP_EE4typeEEEEDaOT0_OT1_
200
201
   private:
202
      template <size_t bytes>
203
0
      constexpr std::array<uint8_t, bytes> take() {
204
0
         m_bytes_consumed += bytes;
205
0
         if(m_bytes_consumed > bound) {
206
0
            throw Internal_Error("XOF consumed more bytes than allowed");
207
0
         }
208
0
         return m_xof.template output<bytes>();
209
0
      }
Unexecuted instantiation: std::__1::array<unsigned char, 8ul> Botan::detail::Bounded_XOF<Botan::XOF&, 229ul>::take<8ul>()
Unexecuted instantiation: std::__1::array<unsigned char, 1ul> Botan::detail::Bounded_XOF<Botan::XOF&, 229ul>::take<1ul>()
Unexecuted instantiation: std::__1::array<unsigned char, 3ul> Botan::detail::Bounded_XOF<Botan::XOF&, 894ul>::take<3ul>()
Unexecuted instantiation: std::__1::array<unsigned char, 1ul> Botan::detail::Bounded_XOF<Botan::XOF&, 481ul>::take<1ul>()
Unexecuted instantiation: std::__1::array<unsigned char, 3ul> Botan::detail::Bounded_XOF<Botan::XOF&, 840ul>::take<3ul>()
210
211
   private:
212
      XofT m_xof;
213
      size_t m_bytes_consumed;
214
};
215
216
}  // namespace detail
217
218
class XOF;
219
220
template <size_t bound>
221
using Bounded_XOF = detail::Bounded_XOF<XOF&, bound>;
222
223
}  // namespace Botan
224
225
#endif