Botan 3.6.1
Crypto and TLS for C&
pcurves_wrap.h
Go to the documentation of this file.
1/*
2* (C) 2024 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#ifndef BOTAN_PCURVES_WRAP_H_
8#define BOTAN_PCURVES_WRAP_H_
9
10#include <botan/internal/pcurves.h>
11#include <botan/internal/pcurves_impl.h>
12
13namespace Botan::PCurve {
14
15template <typename C>
16concept curve_supports_scalar_invert = requires(const typename C::Scalar& s) {
17 { C::scalar_invert(s) } -> std::same_as<typename C::Scalar>;
18};
19
20template <typename C>
22 public:
24 public:
25 static constexpr size_t WindowBits = 3;
26
27 const WindowedMul2Table<C, WindowBits>& table() const { return m_table; }
28
29 explicit PrecomputedMul2TableC(const typename C::AffinePoint& x, const typename C::AffinePoint& y) :
30 m_table(x, y) {}
31
32 private:
33 WindowedMul2Table<C, WindowBits> m_table;
34 };
35
36 static_assert(C::OrderBits <= PrimeOrderCurve::MaximumBitLength);
37 static_assert(C::PrimeFieldBits <= PrimeOrderCurve::MaximumBitLength);
38
39 size_t order_bits() const override { return C::OrderBits; }
40
41 size_t scalar_bytes() const override { return C::Scalar::BYTES; }
42
43 size_t field_element_bytes() const override { return C::FieldElement::BYTES; }
44
45 ProjectivePoint mul_by_g(const Scalar& scalar, RandomNumberGenerator& rng) const override {
46 return stash(m_mul_by_g.mul(from_stash(scalar), rng));
47 }
48
49 ProjectivePoint mul(const AffinePoint& pt, const Scalar& scalar, RandomNumberGenerator& rng) const override {
50 auto tbl = WindowedMulTable<C, 4>(from_stash(pt));
51 return stash(tbl.mul(from_stash(scalar), rng));
52 }
53
54 std::unique_ptr<const PrecomputedMul2Table> mul2_setup(const AffinePoint& x,
55 const AffinePoint& y) const override {
56 return std::make_unique<PrecomputedMul2TableC>(from_stash(x), from_stash(y));
57 }
58
59 std::optional<ProjectivePoint> mul2_vartime(const PrecomputedMul2Table& tableb,
60 const Scalar& s1,
61 const Scalar& s2) const override {
62 try {
63 const auto& table = dynamic_cast<const PrecomputedMul2TableC&>(tableb);
64 auto pt = table.table().mul2_vartime(from_stash(s1), from_stash(s2));
65 if(pt.is_identity().as_bool()) {
66 return {};
67 } else {
68 return stash(pt);
69 }
70 } catch(std::bad_cast&) {
71 throw Invalid_Argument("Curve mismatch");
72 }
73 }
74
76 const Scalar& v,
77 const Scalar& s1,
78 const Scalar& s2) const override {
79 try {
80 const auto& table = dynamic_cast<const PrecomputedMul2TableC&>(tableb);
81 const auto pt = table.table().mul2_vartime(from_stash(s1), from_stash(s2));
82 // Variable time here, so the early return is fine
83 if(pt.is_identity().as_bool()) {
84 return false;
85 }
86
87 /*
88 * Avoid the inversion by instead projecting v.
89 *
90 * Given (x*z2) and v we want to know if x % n == v
91 *
92 * Inverting z2 to extract x is expensive. Instead compute (v*z2) and
93 * compare it with (x*z2).
94 *
95 * With overwhelming probability, this conversion is correct. The
96 * only time it is not is in the extremely unlikely case where the
97 * signer actually reduced the x coordinate modulo the group order.
98 * That is handled seperately in a second step.
99 */
100 const auto z2 = pt.z().square();
101
102 std::array<uint8_t, C::Scalar::BYTES> v_bytes;
103 from_stash(v).serialize_to(v_bytes);
104
105 if(const auto fe_v = C::FieldElement::deserialize(v_bytes)) {
106 if((*fe_v * z2 == pt.x()).as_bool()) {
107 return true;
108 }
109
110 /*
111 * Possibly (if cryptographically unlikely) the signer
112 * reduced the value modulo the group order.
113 *
114 * If so we must check v + n similarly as before. However here
115 * we must be careful to not overflow since otherwise that
116 * would lead to us accepting an incorrect signature.
117 *
118 * If the order is > p then the reduction modulo p would not have
119 * had any effect and we don't need to consider the possibility
120 */
121 if constexpr(C::OrderIsLessThanField) {
122 /*
123 * We have to be careful to avoid overflow since this would
124 * lead to a forgery
125 *
126 * v < (p)-n => v + n < p
127 *
128 * The values n and neg_n could be precomputed but they are
129 * fast to compute and this codepath will ~never be taken
130 * unless when verifying an invalid signature. In any case
131 * it is many times cheaper than performing the modular inversion
132 * which this approach avoids.
133 */
134
135 // Create the group order as a field element, safe because n < p
136 const auto n = C::FieldElement::from_words(C::NW);
137 const auto neg_n = n.negate().to_words();
138
139 const auto vw = fe_v->to_words();
140 if(bigint_ct_is_lt(vw.data(), vw.size(), neg_n.data(), neg_n.size()).as_bool()) {
141 return (((*fe_v + n) * z2) == pt.x()).as_bool();
142 }
143 }
144 }
145
146 return false;
147 } catch(std::bad_cast&) {
148 throw Invalid_Argument("Curve mismatch");
149 }
150 }
151
152 Scalar base_point_mul_x_mod_order(const Scalar& scalar, RandomNumberGenerator& rng) const override {
153 auto pt = m_mul_by_g.mul(from_stash(scalar), rng);
154 std::array<uint8_t, C::FieldElement::BYTES> x_bytes;
155 to_affine_x<C>(pt).serialize_to(std::span{x_bytes});
156 return stash(C::Scalar::from_wide_bytes(std::span<const uint8_t, C::FieldElement::BYTES>{x_bytes}));
157 }
158
159 AffinePoint generator() const override { return stash(C::G); }
160
161 AffinePoint point_to_affine(const ProjectivePoint& pt) const override {
162 return stash(to_affine<C>(from_stash(pt)));
163 }
164
166 return stash(C::ProjectivePoint::from_affine(from_stash(pt)));
167 }
168
169 ProjectivePoint point_double(const ProjectivePoint& pt) const override { return stash(from_stash(pt).dbl()); }
170
171 ProjectivePoint point_add(const ProjectivePoint& a, const ProjectivePoint& b) const override {
172 return stash(from_stash(a) + from_stash(b));
173 }
174
176 return stash(from_stash(a) + from_stash(b));
177 }
178
179 ProjectivePoint point_negate(const ProjectivePoint& pt) const override { return stash(from_stash(pt).negate()); }
180
181 bool affine_point_is_identity(const AffinePoint& pt) const override {
182 return from_stash(pt).is_identity().as_bool();
183 }
184
185 void serialize_point(std::span<uint8_t> bytes, const AffinePoint& pt) const override {
186 BOTAN_ARG_CHECK(bytes.size() == C::AffinePoint::BYTES, "Invalid length for serialize_point");
187 from_stash(pt).serialize_to(bytes.subspan<0, C::AffinePoint::BYTES>());
188 }
189
190 void serialize_point_compressed(std::span<uint8_t> bytes, const AffinePoint& pt) const override {
191 BOTAN_ARG_CHECK(bytes.size() == C::AffinePoint::COMPRESSED_BYTES,
192 "Invalid length for serialize_point_compressed");
193 from_stash(pt).serialize_compressed_to(bytes.subspan<0, C::AffinePoint::COMPRESSED_BYTES>());
194 }
195
196 void serialize_point_x(std::span<uint8_t> bytes, const AffinePoint& pt) const override {
197 BOTAN_ARG_CHECK(bytes.size() == C::FieldElement::BYTES, "Invalid length for serialize_point_x");
198 from_stash(pt).serialize_x_to(bytes.subspan<0, C::FieldElement::BYTES>());
199 }
200
201 void serialize_scalar(std::span<uint8_t> bytes, const Scalar& scalar) const override {
202 BOTAN_ARG_CHECK(bytes.size() == C::Scalar::BYTES, "Invalid length to serialize_scalar");
203 return from_stash(scalar).serialize_to(bytes.subspan<0, C::Scalar::BYTES>());
204 }
205
206 std::optional<Scalar> deserialize_scalar(std::span<const uint8_t> bytes) const override {
207 if(auto scalar = C::Scalar::deserialize(bytes)) {
208 if(!scalar->is_zero().as_bool()) {
209 return stash(*scalar);
210 }
211 }
212
213 return {};
214 }
215
216 std::optional<Scalar> scalar_from_wide_bytes(std::span<const uint8_t> bytes) const override {
217 if(auto s = C::Scalar::from_wide_bytes_varlen(bytes)) {
218 return stash(*s);
219 } else {
220 return {};
221 }
222 }
223
224 std::optional<AffinePoint> deserialize_point(std::span<const uint8_t> bytes) const override {
225 if(auto pt = C::AffinePoint::deserialize(bytes)) {
226 return stash(*pt);
227 } else {
228 return {};
229 }
230 }
231
232 AffinePoint hash_to_curve_nu(std::string_view hash,
233 std::span<const uint8_t> input,
234 std::span<const uint8_t> domain_sep) const override {
235 if constexpr(C::ValidForSswuHash) {
236 return stash(hash_to_curve_sswu<C, false>(hash, input, domain_sep));
237 } else {
238 throw Not_Implemented("Hash to curve is not implemented for this curve");
239 }
240 }
241
242 ProjectivePoint hash_to_curve_ro(std::string_view hash,
243 std::span<const uint8_t> input,
244 std::span<const uint8_t> domain_sep) const override {
245 if constexpr(C::ValidForSswuHash) {
246 return stash(hash_to_curve_sswu<C, true>(hash, input, domain_sep));
247 } else {
248 throw Not_Implemented("Hash to curve is not implemented for this curve");
249 }
250 }
251
252 Scalar scalar_add(const Scalar& a, const Scalar& b) const override {
253 return stash(from_stash(a) + from_stash(b));
254 }
255
256 Scalar scalar_sub(const Scalar& a, const Scalar& b) const override {
257 return stash(from_stash(a) - from_stash(b));
258 }
259
260 Scalar scalar_mul(const Scalar& a, const Scalar& b) const override {
261 return stash(from_stash(a) * from_stash(b));
262 }
263
264 Scalar scalar_square(const Scalar& s) const override { return stash(from_stash(s).square()); }
265
266 Scalar scalar_invert(const Scalar& ss) const override {
267 auto s = from_stash(ss);
268 if constexpr(curve_supports_scalar_invert<C>) {
269 return stash(C::scalar_invert(s));
270 } else {
271 return stash(s.invert());
272 }
273 }
274
275 Scalar scalar_negate(const Scalar& s) const override { return stash(from_stash(s).negate()); }
276
277 bool scalar_is_zero(const Scalar& s) const override { return from_stash(s).is_zero().as_bool(); }
278
279 bool scalar_equal(const Scalar& a, const Scalar& b) const override {
280 return (from_stash(a) == from_stash(b)).as_bool();
281 }
282
283 Scalar scalar_zero() const override { return stash(C::Scalar::zero()); }
284
285 Scalar scalar_one() const override { return stash(C::Scalar::one()); }
286
287 Scalar scalar_from_u32(uint32_t x) const override { return stash(C::Scalar::from_word(x)); }
288
289 Scalar random_scalar(RandomNumberGenerator& rng) const override { return stash(C::Scalar::random(rng)); }
290
291 PrimeOrderCurveImpl() : m_mul_by_g(C::G) {}
292
293 static std::shared_ptr<const PrimeOrderCurve> instance() {
294 static auto g_curve = std::make_shared<const PrimeOrderCurveImpl<C>>();
295 return g_curve;
296 }
297
298 private:
299 static Scalar stash(const typename C::Scalar& s) {
300 return Scalar::_create(instance(), s.template stash_value<StorageWords>());
301 }
302
303 static typename C::Scalar from_stash(const Scalar& s) {
304 if(s._curve() != instance()) {
305 throw Invalid_Argument("Curve mismatch");
306 }
307 return C::Scalar::from_stash(s._value());
308 }
309
310 static AffinePoint stash(const typename C::AffinePoint& pt) {
311 auto x_w = pt.x().template stash_value<StorageWords>();
312 auto y_w = pt.y().template stash_value<StorageWords>();
313 return AffinePoint::_create(instance(), x_w, y_w);
314 }
315
316 static typename C::AffinePoint from_stash(const AffinePoint& pt) {
317 if(pt._curve() != instance()) {
318 throw Invalid_Argument("Curve mismatch");
319 }
320 auto x = C::FieldElement::from_stash(pt._x());
321 auto y = C::FieldElement::from_stash(pt._y());
322 return typename C::AffinePoint(x, y);
323 }
324
325 static ProjectivePoint stash(const typename C::ProjectivePoint& pt) {
326 auto x_w = pt.x().template stash_value<StorageWords>();
327 auto y_w = pt.y().template stash_value<StorageWords>();
328 auto z_w = pt.z().template stash_value<StorageWords>();
329 return ProjectivePoint::_create(instance(), x_w, y_w, z_w);
330 }
331
332 static typename C::ProjectivePoint from_stash(const ProjectivePoint& pt) {
333 if(pt._curve() != instance()) {
334 throw Invalid_Argument("Curve mismatch");
335 }
336 auto x = C::FieldElement::from_stash(pt._x());
337 auto y = C::FieldElement::from_stash(pt._y());
338 auto z = C::FieldElement::from_stash(pt._z());
339 return typename C::ProjectivePoint(x, y, z);
340 }
341
342 private:
343 const PrecomputedBaseMulTable<C, 5> m_mul_by_g;
344};
345
346} // namespace Botan::PCurve
347
348#endif
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
PrecomputedMul2TableC(const typename C::AffinePoint &x, const typename C::AffinePoint &y)
const WindowedMul2Table< C, WindowBits > & table() const
Scalar scalar_invert(const Scalar &ss) const override
size_t field_element_bytes() const override
Scalar scalar_zero() const override
ProjectivePoint hash_to_curve_ro(std::string_view hash, std::span< const uint8_t > input, std::span< const uint8_t > domain_sep) const override
bool scalar_equal(const Scalar &a, const Scalar &b) const override
void serialize_scalar(std::span< uint8_t > bytes, const Scalar &scalar) const override
bool affine_point_is_identity(const AffinePoint &pt) const override
Scalar scalar_from_u32(uint32_t x) const override
Scalar scalar_sub(const Scalar &a, const Scalar &b) const override
size_t order_bits() const override
Return the bit length of the group order.
void serialize_point_x(std::span< uint8_t > bytes, const AffinePoint &pt) const override
AffinePoint generator() const override
Return the standard generator.
ProjectivePoint mul_by_g(const Scalar &scalar, RandomNumberGenerator &rng) const override
size_t scalar_bytes() const override
Return the byte length of the scalar element.
bool scalar_is_zero(const Scalar &s) const override
Scalar scalar_one() const override
ProjectivePoint point_negate(const ProjectivePoint &pt) const override
bool mul2_vartime_x_mod_order_eq(const PrecomputedMul2Table &tableb, const Scalar &v, const Scalar &s1, const Scalar &s2) const override
static std::shared_ptr< const PrimeOrderCurve > instance()
std::optional< Scalar > scalar_from_wide_bytes(std::span< const uint8_t > bytes) const override
std::unique_ptr< const PrecomputedMul2Table > mul2_setup(const AffinePoint &x, const AffinePoint &y) const override
Setup a table for 2-ary multiplication.
Scalar base_point_mul_x_mod_order(const Scalar &scalar, RandomNumberGenerator &rng) const override
ProjectivePoint point_double(const ProjectivePoint &pt) const override
std::optional< Scalar > deserialize_scalar(std::span< const uint8_t > bytes) const override
Scalar scalar_add(const Scalar &a, const Scalar &b) const override
AffinePoint hash_to_curve_nu(std::string_view hash, std::span< const uint8_t > input, std::span< const uint8_t > domain_sep) const override
Scalar scalar_negate(const Scalar &s) const override
std::optional< ProjectivePoint > mul2_vartime(const PrecomputedMul2Table &tableb, const Scalar &s1, const Scalar &s2) const override
std::optional< AffinePoint > deserialize_point(std::span< const uint8_t > bytes) const override
Scalar random_scalar(RandomNumberGenerator &rng) const override
ProjectivePoint point_add(const ProjectivePoint &a, const ProjectivePoint &b) const override
void serialize_point(std::span< uint8_t > bytes, const AffinePoint &pt) const override
Scalar scalar_square(const Scalar &s) const override
ProjectivePoint point_to_projective(const AffinePoint &pt) const override
Scalar scalar_mul(const Scalar &a, const Scalar &b) const override
AffinePoint point_to_affine(const ProjectivePoint &pt) const override
ProjectivePoint point_add_mixed(const ProjectivePoint &a, const AffinePoint &b) const override
ProjectivePoint mul(const AffinePoint &pt, const Scalar &scalar, RandomNumberGenerator &rng) const override
void serialize_point_compressed(std::span< uint8_t > bytes, const AffinePoint &pt) const override
static const size_t MaximumBitLength
Definition pcurves.h:37
int(* final)(unsigned char *, CTX *)
BigInt square(const BigInt &x)
Definition numthry.cpp:157
constexpr auto bigint_ct_is_lt(const W x[], size_t x_size, const W y[], size_t y_size, bool lt_or_equal=false) -> CT::Mask< W >
Definition mp_core.h:639
const SIMD_8x32 & b