/src/botan/build/include/internal/botan/internal/pcurves.h
Line | Count | Source (jump to first uncovered line) |
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_H_ |
8 | | #define BOTAN_PCURVES_H_ |
9 | | |
10 | | #include <botan/internal/pcurves_id.h> |
11 | | |
12 | | #include <botan/concepts.h> |
13 | | #include <botan/secmem.h> |
14 | | #include <botan/types.h> |
15 | | #include <array> |
16 | | #include <optional> |
17 | | #include <span> |
18 | | #include <string_view> |
19 | | #include <vector> |
20 | | |
21 | | namespace Botan { |
22 | | |
23 | | class RandomNumberGenerator; |
24 | | |
25 | | } // namespace Botan |
26 | | |
27 | | namespace Botan::PCurve { |
28 | | |
29 | | /** |
30 | | * An elliptic curve without cofactor in Weierstrass form |
31 | | */ |
32 | | class BOTAN_TEST_API PrimeOrderCurve { |
33 | | public: |
34 | | /// Somewhat arbitrary maximum size for a field or scalar |
35 | | /// |
36 | | /// Sized to fit at least P-521 |
37 | | static const size_t MaximumBitLength = 521; |
38 | | |
39 | | static const size_t MaximumByteLength = (MaximumBitLength + 7) / 8; |
40 | | |
41 | | /// Number of words used to store MaximumByteLength |
42 | | static const size_t StorageWords = (MaximumByteLength + sizeof(word) - 1) / sizeof(word); |
43 | | |
44 | 0 | static std::shared_ptr<const PrimeOrderCurve> from_name(std::string_view name) { |
45 | 0 | if(auto id = PrimeOrderCurveId::from_string(name)) { |
46 | 0 | return PrimeOrderCurve::from_id(id.value()); |
47 | 0 | } else { |
48 | 0 | return {}; |
49 | 0 | } |
50 | 0 | } |
51 | | |
52 | | static std::shared_ptr<const PrimeOrderCurve> from_id(PrimeOrderCurveId id); |
53 | | |
54 | | typedef std::array<word, StorageWords> StorageUnit; |
55 | | typedef std::shared_ptr<const PrimeOrderCurve> CurvePtr; |
56 | | |
57 | | /// Elliptic curve scalar |
58 | | /// |
59 | | /// This refers to the set of integers modulo the (prime) group order |
60 | | /// of the elliptic curve. |
61 | | class Scalar final { |
62 | | public: |
63 | 22.3k | Scalar(const Scalar& other) = default; |
64 | 68.1k | Scalar(Scalar&& other) = default; |
65 | 0 | Scalar& operator=(const Scalar& other) = default; |
66 | 0 | Scalar& operator=(Scalar&& other) = default; |
67 | 124k | ~Scalar() = default; |
68 | | |
69 | | /** |
70 | | * Return the size of the byte encoding of Scalars |
71 | | */ |
72 | 0 | size_t bytes() const { return m_curve->scalar_bytes(); } |
73 | | |
74 | | /** |
75 | | * Return the fixed length serialization of this scalar |
76 | | */ |
77 | | template <concepts::resizable_byte_buffer T = std::vector<uint8_t>> |
78 | | T serialize() const { |
79 | | T bytes(this->bytes()); |
80 | | m_curve->serialize_scalar(bytes, *this); |
81 | | return bytes; |
82 | | } |
83 | | |
84 | | /** |
85 | | * Perform integer multiplication modulo the group order |
86 | | */ |
87 | 392 | friend Scalar operator*(const Scalar& a, const Scalar& b) { return a.m_curve->scalar_mul(a, b); } |
88 | | |
89 | | /** |
90 | | * Perform integer addition modulo the group order |
91 | | */ |
92 | 0 | friend Scalar operator+(const Scalar& a, const Scalar& b) { return a.m_curve->scalar_add(a, b); } |
93 | | |
94 | | /** |
95 | | * Perform integer subtraction modulo the group order |
96 | | */ |
97 | 0 | friend Scalar operator-(const Scalar& a, const Scalar& b) { return a.m_curve->scalar_sub(a, b); } |
98 | | |
99 | | /** |
100 | | * Check for equality |
101 | | */ |
102 | 0 | friend bool operator==(const Scalar& a, const Scalar& b) { return a.m_curve->scalar_equal(a, b); } |
103 | | |
104 | | /** |
105 | | * Negate modulo the group order (ie return p - *this where p is the group order) |
106 | | */ |
107 | 0 | Scalar negate() const { return m_curve->scalar_negate(*this); } |
108 | | |
109 | | /** |
110 | | * Square modulo the group order |
111 | | */ |
112 | 0 | Scalar square() const { return m_curve->scalar_square(*this); } |
113 | | |
114 | | /** |
115 | | * Return the modular inverse of *this |
116 | | * |
117 | | * If *this is zero then returns zero. |
118 | | */ |
119 | 196 | Scalar invert() const { return m_curve->scalar_invert(*this); } |
120 | | |
121 | | /** |
122 | | * Returns true if this is equal to zero |
123 | | */ |
124 | 392 | bool is_zero() const { return m_curve->scalar_is_zero(*this); } |
125 | | |
126 | 45.7k | const auto& _curve() const { return m_curve; } |
127 | | |
128 | 45.7k | const auto& _value() const { return m_value; } |
129 | | |
130 | 33.6k | static Scalar _create(CurvePtr curve, StorageUnit v) { return Scalar(std::move(curve), v); } |
131 | | |
132 | | private: |
133 | 33.6k | Scalar(CurvePtr curve, StorageUnit v) : m_curve(std::move(curve)), m_value(v) {} |
134 | | |
135 | | CurvePtr m_curve; |
136 | | StorageUnit m_value; |
137 | | }; |
138 | | |
139 | | /** |
140 | | * A point on the elliptic curve in affine form |
141 | | * |
142 | | * These points can be serialized, or converted to projective form for computation |
143 | | */ |
144 | | class AffinePoint final { |
145 | | public: |
146 | 0 | AffinePoint(const AffinePoint& other) = default; |
147 | 108k | AffinePoint(AffinePoint&& other) = default; |
148 | | AffinePoint& operator=(const AffinePoint& other) = default; |
149 | | AffinePoint& operator=(AffinePoint&& other) = default; |
150 | 155k | ~AffinePoint() = default; |
151 | | |
152 | 0 | static AffinePoint generator(CurvePtr curve) { return curve->generator(); } |
153 | | |
154 | | /** |
155 | | * Return the size of the uncompressed encoding of points |
156 | | */ |
157 | 46.5k | size_t bytes() const { return 1 + 2 * m_curve->field_element_bytes(); } |
158 | | |
159 | | /** |
160 | | * Return the size of the compressed encoding of points |
161 | | */ |
162 | 0 | size_t compressed_bytes() const { return 1 + m_curve->field_element_bytes(); } |
163 | | |
164 | | /** |
165 | | * Return the serialization of the point in uncompressed form |
166 | | */ |
167 | | template <concepts::resizable_byte_buffer T = std::vector<uint8_t>> |
168 | 46.5k | T serialize() const { |
169 | 46.5k | T bytes(this->bytes()); |
170 | 46.5k | m_curve->serialize_point(bytes, *this); |
171 | 46.5k | return bytes; |
172 | 46.5k | } |
173 | | |
174 | | /** |
175 | | * Return the serialization of the point in compressed form |
176 | | */ |
177 | | template <concepts::resizable_byte_buffer T = std::vector<uint8_t>> |
178 | | T serialize_compressed() const { |
179 | | T bytes(this->compressed_bytes()); |
180 | | m_curve->serialize_point_compressed(bytes, *this); |
181 | | return bytes; |
182 | | } |
183 | | |
184 | | /** |
185 | | * Return the serialization of the x coordinate |
186 | | */ |
187 | | template <concepts::resizable_byte_buffer T = secure_vector<uint8_t>> |
188 | | T x_bytes() const { |
189 | | secure_vector<uint8_t> bytes(m_curve->field_element_bytes()); |
190 | | m_curve->serialize_point_x(bytes, *this); |
191 | | return bytes; |
192 | | } |
193 | | |
194 | | /** |
195 | | * Point negation |
196 | | */ |
197 | 0 | AffinePoint negate() const { return m_curve->point_negate(*this); } |
198 | | |
199 | | /** |
200 | | * Return true if this is the curve identity element (aka the point at infinity) |
201 | | */ |
202 | 47.2k | bool is_identity() const { return m_curve->affine_point_is_identity(*this); } |
203 | | |
204 | 106k | const auto& _curve() const { return m_curve; } |
205 | | |
206 | 106k | const auto& _x() const { return m_x; } |
207 | | |
208 | 106k | const auto& _y() const { return m_y; } |
209 | | |
210 | 47.2k | static AffinePoint _create(CurvePtr curve, StorageUnit x, StorageUnit y) { |
211 | 47.2k | return AffinePoint(std::move(curve), x, y); |
212 | 47.2k | } |
213 | | |
214 | | private: |
215 | 47.2k | AffinePoint(CurvePtr curve, StorageUnit x, StorageUnit y) : m_curve(std::move(curve)), m_x(x), m_y(y) {} |
216 | | |
217 | | CurvePtr m_curve; |
218 | | StorageUnit m_x; |
219 | | StorageUnit m_y; |
220 | | }; |
221 | | |
222 | | /** |
223 | | * A point on the elliptic curve in projective form |
224 | | * |
225 | | * This is a form that is convenient for computation; it must be converted to |
226 | | * affine form for comparisons or serialization. |
227 | | */ |
228 | | class ProjectivePoint final { |
229 | | public: |
230 | | ProjectivePoint(const ProjectivePoint& other) = default; |
231 | 0 | ProjectivePoint(ProjectivePoint&& other) = default; |
232 | | ProjectivePoint& operator=(const ProjectivePoint& other) = default; |
233 | | ProjectivePoint& operator=(ProjectivePoint&& other) = default; |
234 | 32.5k | ~ProjectivePoint() = default; |
235 | | |
236 | | /** |
237 | | * Convert a point from affine to projective form |
238 | | */ |
239 | 0 | static ProjectivePoint from_affine(const AffinePoint& pt) { return pt._curve()->point_to_projective(pt); } |
240 | | |
241 | | /** |
242 | | * Convert a point from projective to affine form |
243 | | * |
244 | | * This operation is expensive; perform it only when required for |
245 | | * serialization |
246 | | */ |
247 | 32.5k | AffinePoint to_affine() const { return m_curve->point_to_affine(*this); } |
248 | | |
249 | 0 | ProjectivePoint dbl() const { return m_curve->point_double(*this); } |
250 | | |
251 | 0 | friend ProjectivePoint operator+(const ProjectivePoint& x, const ProjectivePoint& y) { |
252 | 0 | return x.m_curve->point_add(x, y); |
253 | 0 | } |
254 | | |
255 | 0 | friend ProjectivePoint operator+(const ProjectivePoint& x, const AffinePoint& y) { |
256 | 0 | return x.m_curve->point_add_mixed(x, y); |
257 | 0 | } |
258 | | |
259 | 32.5k | const auto& _curve() const { return m_curve; } |
260 | | |
261 | 32.5k | const auto& _x() const { return m_x; } |
262 | | |
263 | 32.5k | const auto& _y() const { return m_y; } |
264 | | |
265 | 32.5k | const auto& _z() const { return m_z; } |
266 | | |
267 | 32.5k | static ProjectivePoint _create(CurvePtr curve, StorageUnit x, StorageUnit y, StorageUnit z) { |
268 | 32.5k | return ProjectivePoint(std::move(curve), x, y, z); |
269 | 32.5k | } |
270 | | |
271 | | private: |
272 | | ProjectivePoint(CurvePtr curve, StorageUnit x, StorageUnit y, StorageUnit z) : |
273 | 32.5k | m_curve(std::move(curve)), m_x(x), m_y(y), m_z(z) {} |
274 | | |
275 | | CurvePtr m_curve; |
276 | | StorageUnit m_x; |
277 | | StorageUnit m_y; |
278 | | StorageUnit m_z; |
279 | | }; |
280 | | |
281 | | class PrecomputedMul2Table { |
282 | | public: |
283 | 330 | virtual ~PrecomputedMul2Table() = default; |
284 | | }; |
285 | | |
286 | 38 | virtual ~PrimeOrderCurve() = default; |
287 | | |
288 | | /// Return the bit length of the group order |
289 | | virtual size_t order_bits() const = 0; |
290 | | |
291 | | /// Return the byte length of the scalar element |
292 | | virtual size_t scalar_bytes() const = 0; |
293 | | |
294 | | /// Return the byte length of a field element |
295 | | /// |
296 | | /// Each point consists of two field elements |
297 | | virtual size_t field_element_bytes() const = 0; |
298 | | |
299 | | /// Base point multiplication |
300 | | /// |
301 | | /// Multiply by the standard generator point g |
302 | | virtual ProjectivePoint mul_by_g(const Scalar& scalar, RandomNumberGenerator& rng) const = 0; |
303 | | |
304 | | /// Base point multiplication, returning only the x coordinate modulo the group order |
305 | | /// |
306 | | /// Multiply by the standard generator point g, then extract the x |
307 | | /// coordinate as an integer, then reduce the x coordinate modulo the |
308 | | /// group order |
309 | | virtual Scalar base_point_mul_x_mod_order(const Scalar& scalar, RandomNumberGenerator& rng) const = 0; |
310 | | |
311 | | /// Generic point multiplication |
312 | | /// |
313 | | /// Multiply an arbitrary point by a scalar |
314 | | virtual ProjectivePoint mul(const AffinePoint& pt, const Scalar& scalar, RandomNumberGenerator& rng) const = 0; |
315 | | |
316 | | /// Setup a table for 2-ary multiplication |
317 | | virtual std::unique_ptr<const PrecomputedMul2Table> mul2_setup(const AffinePoint& pt1, |
318 | | const AffinePoint& pt2) const = 0; |
319 | | |
320 | | /// Perform 2-ary multiplication (variable time) |
321 | | /// |
322 | | /// Compute s1*pt1 + s2*pt2 in variable time |
323 | | /// |
324 | | /// Returns nullopt if the produced point is the point at infinity |
325 | | virtual std::optional<ProjectivePoint> mul2_vartime(const PrecomputedMul2Table& table, |
326 | | const Scalar& s1, |
327 | | const Scalar& s2) const = 0; |
328 | | |
329 | | /// Perform 2-ary multiplication (constant time) |
330 | | /// |
331 | | /// Compute p*x + q*y |
332 | | /// |
333 | | /// Returns nullopt if the produced point is the point at infinity |
334 | | virtual std::optional<ProjectivePoint> mul_px_qy(const AffinePoint& p, |
335 | | const Scalar& x, |
336 | | const AffinePoint& q, |
337 | | const Scalar& y, |
338 | | RandomNumberGenerator& rng) const = 0; |
339 | | |
340 | | /// Perform 2-ary multiplication (variable time), reducing x modulo order |
341 | | /// |
342 | | /// Compute s1*pt1 + s2*pt2 in variable time, then extract the x |
343 | | /// coordinate of the result, and reduce x modulo the group order. Compare |
344 | | /// that value with v. If equal, returns true. Otherwise returns false, |
345 | | /// including if the produced point is the point at infinity |
346 | | virtual bool mul2_vartime_x_mod_order_eq(const PrecomputedMul2Table& table, |
347 | | const Scalar& v, |
348 | | const Scalar& s1, |
349 | | const Scalar& s2) const = 0; |
350 | | |
351 | | /// Return the standard generator |
352 | | virtual AffinePoint generator() const = 0; |
353 | | |
354 | | /// Deserialize a point |
355 | | /// |
356 | | /// Both compressed and uncompressed encodings are accepted |
357 | | /// |
358 | | /// Note that the deprecated "hybrid" encoding is not supported here |
359 | | virtual std::optional<AffinePoint> deserialize_point(std::span<const uint8_t> bytes) const = 0; |
360 | | |
361 | | /// Deserialize a scalar |
362 | | /// |
363 | | /// This function requires the input length be exactly scalar_bytes long; |
364 | | /// it does not accept inputs that are shorter, or with excess leading |
365 | | /// zero padding bytes. |
366 | | virtual std::optional<Scalar> deserialize_scalar(std::span<const uint8_t> bytes) const = 0; |
367 | | |
368 | | /// Reduce an integer modulo the group order |
369 | | /// |
370 | | /// The input can be at most twice the bit length of the order; if larger than this |
371 | | /// nullopt is returned |
372 | | virtual std::optional<Scalar> scalar_from_wide_bytes(std::span<const uint8_t> bytes) const = 0; |
373 | | |
374 | | virtual AffinePoint point_to_affine(const ProjectivePoint& pt) const = 0; |
375 | | |
376 | | virtual ProjectivePoint point_to_projective(const AffinePoint& pt) const = 0; |
377 | | |
378 | | virtual bool affine_point_is_identity(const AffinePoint& pt) const = 0; |
379 | | |
380 | | virtual ProjectivePoint point_double(const ProjectivePoint& pt) const = 0; |
381 | | |
382 | | virtual AffinePoint point_negate(const AffinePoint& pt) const = 0; |
383 | | |
384 | | virtual ProjectivePoint point_add(const ProjectivePoint& a, const ProjectivePoint& b) const = 0; |
385 | | |
386 | | virtual ProjectivePoint point_add_mixed(const ProjectivePoint& a, const AffinePoint& b) const = 0; |
387 | | |
388 | | virtual void serialize_point(std::span<uint8_t> bytes, const AffinePoint& pt) const = 0; |
389 | | |
390 | | virtual void serialize_point_compressed(std::span<uint8_t> bytes, const AffinePoint& pt) const = 0; |
391 | | |
392 | | virtual void serialize_point_x(std::span<uint8_t> bytes, const AffinePoint& pt) const = 0; |
393 | | |
394 | | virtual void serialize_scalar(std::span<uint8_t> bytes, const Scalar& scalar) const = 0; |
395 | | |
396 | | /** |
397 | | * Return the scalar zero |
398 | | */ |
399 | | virtual Scalar scalar_zero() const = 0; |
400 | | |
401 | | /** |
402 | | * Return the scalar one |
403 | | */ |
404 | | virtual Scalar scalar_one() const = 0; |
405 | | |
406 | | /** |
407 | | * Return a small scalar |
408 | | */ |
409 | | virtual Scalar scalar_from_u32(uint32_t x) const = 0; |
410 | | |
411 | | virtual Scalar scalar_add(const Scalar& a, const Scalar& b) const = 0; |
412 | | virtual Scalar scalar_sub(const Scalar& a, const Scalar& b) const = 0; |
413 | | virtual Scalar scalar_mul(const Scalar& a, const Scalar& b) const = 0; |
414 | | virtual Scalar scalar_square(const Scalar& s) const = 0; |
415 | | virtual Scalar scalar_invert(const Scalar& s) const = 0; |
416 | | virtual Scalar scalar_negate(const Scalar& s) const = 0; |
417 | | virtual bool scalar_is_zero(const Scalar& s) const = 0; |
418 | | virtual bool scalar_equal(const Scalar& a, const Scalar& b) const = 0; |
419 | | |
420 | | /** |
421 | | * Return a new random scalar |
422 | | */ |
423 | | virtual Scalar random_scalar(RandomNumberGenerator& rng) const = 0; |
424 | | |
425 | | /** |
426 | | * RFC 9380 hash to curve (NU variant) |
427 | | * |
428 | | * This is currently only supported for a few specific curves |
429 | | */ |
430 | | virtual AffinePoint hash_to_curve_nu(std::string_view hash, |
431 | | std::span<const uint8_t> input, |
432 | | std::span<const uint8_t> domain_sep) const = 0; |
433 | | |
434 | | /** |
435 | | * RFC 9380 hash to curve (RO variant) |
436 | | * |
437 | | * This is currently only supported for a few specific curves |
438 | | */ |
439 | | virtual ProjectivePoint hash_to_curve_ro(std::string_view hash, |
440 | | std::span<const uint8_t> input, |
441 | | std::span<const uint8_t> domain_sep) const = 0; |
442 | | }; |
443 | | |
444 | | } // namespace Botan::PCurve |
445 | | |
446 | | #endif |