Botan 3.13.0
Crypto and TLS for C&
ec_apoint.cpp
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#include <botan/ec_apoint.h>
8
9#include <botan/ec_group.h>
10#include <botan/ec_scalar.h>
11#include <botan/internal/ec_inner_data.h>
12#include <botan/internal/mem_utils.h>
13
14namespace Botan {
15
16EC_AffinePoint::EC_AffinePoint(std::unique_ptr<EC_AffinePoint_Data> point) : m_point(std::move(point)) {
17 BOTAN_ASSERT_NONNULL(m_point);
18}
19
20EC_AffinePoint::EC_AffinePoint(const EC_AffinePoint& other) : m_point(other.inner().clone()) {}
21
22EC_AffinePoint::EC_AffinePoint(EC_AffinePoint&& other) noexcept : m_point(std::move(other.m_point)) {}
23
25 if(this != &other) {
26 m_point = other.inner().clone();
27 }
28 return (*this);
29}
30
32 m_point.swap(other.m_point);
33 return (*this);
34}
35
36EC_AffinePoint::EC_AffinePoint(const EC_Group& group, std::span<const uint8_t> bytes) {
37 if(auto pt = EC_AffinePoint::deserialize(group, bytes)) {
38 m_point = std::move(pt->m_point);
39 } else {
40 throw Decoding_Error("Failed to deserialize elliptic curve point");
41 }
42}
43
44#if defined(BOTAN_HAS_LEGACY_EC_POINT)
45
46EC_Point EC_AffinePoint::to_legacy_point() const {
47 return m_point->to_legacy_point();
48}
49
50EC_AffinePoint::EC_AffinePoint(const EC_Group& group, const EC_Point& pt) :
51 EC_AffinePoint(group, pt.encode(EC_Point_Format::Uncompressed)) {}
52
53#endif
54
56 if(this == &other) {
57 return true;
58 }
59
60 // We are relying on EC_Group to ensure there is just a single shared_ptr
61 // for any set of group params
62 if(this->_group() != other._group()) {
63 return false;
64 }
65
66 auto a_is_id = this->is_identity();
67 auto b_is_id = other.is_identity();
68
69 if(a_is_id || b_is_id) {
70 return (a_is_id == b_is_id);
71 }
72
73 auto a_xy = this->serialize_uncompressed();
74 auto b_xy = other.serialize_uncompressed();
75 BOTAN_ASSERT_NOMSG(a_xy.size() == b_xy.size());
76
77 return CT::is_equal(a_xy.data(), b_xy.data(), a_xy.size()).as_bool();
78}
79
81 return EC_AffinePoint(group._data()->point_identity());
82}
83
85 // TODO it would be nice to improve this (pcurves supports returning generator directly)
86 if(auto g = EC_AffinePoint::from_bigint_xy(group, group.get_g_x(), group.get_g_y())) {
87 return *g;
88 } else {
89 throw Internal_Error("EC_AffinePoint::generator curve rejected generator");
90 }
91}
92
93std::optional<EC_AffinePoint> EC_AffinePoint::from_bigint_xy(const EC_Group& group, const BigInt& x, const BigInt& y) {
94 if(x.signum() < 0 || x >= group.get_p()) {
95 return {};
96 }
97 if(y.signum() < 0 || y >= group.get_p()) {
98 return {};
99 }
100
101 const size_t fe_bytes = group.get_p_bytes();
102 std::vector<uint8_t> sec1(1 + 2 * fe_bytes);
103 sec1[0] = 0x04;
104 x.serialize_to(std::span{sec1}.subspan(1, fe_bytes));
105 y.serialize_to(std::span{sec1}.last(fe_bytes));
106
108}
109
111 return inner().field_element_bytes();
112}
113
115 return inner().is_identity();
116}
117
119 std::string_view hash_fn,
120 std::span<const uint8_t> input,
121 std::span<const uint8_t> domain_sep) {
122 auto pt = group._data()->point_hash_to_curve_ro(hash_fn, input, domain_sep);
123 return EC_AffinePoint(std::move(pt));
124}
125
127 std::string_view hash_fn,
128 std::span<const uint8_t> input,
129 std::string_view domain_sep) {
130 return EC_AffinePoint::hash_to_curve_ro(group, hash_fn, input, as_span_of_bytes(domain_sep));
131}
132
134 std::string_view hash_fn,
135 std::span<const uint8_t> input,
136 std::span<const uint8_t> domain_sep) {
137 auto pt = group._data()->point_hash_to_curve_nu(hash_fn, input, domain_sep);
138 return EC_AffinePoint(std::move(pt));
139}
140
142 std::string_view hash_fn,
143 std::span<const uint8_t> input,
144 std::string_view domain_sep) {
145 return EC_AffinePoint::hash_to_curve_nu(group, hash_fn, input, as_span_of_bytes(domain_sep));
146}
147
149
150std::optional<EC_AffinePoint> EC_AffinePoint::deserialize(const EC_Group& group, std::span<const uint8_t> bytes) {
151 if(bytes.empty()) {
152 return {};
153 }
154
155 switch(bytes[0]) {
156 case 0x00:
157 // The identity element (see SEC1 section 2.3.4)
158 // TODO(Botan4) remove this - we should reject the identity encoding
159 if(bytes.size() == 1) {
160 return EC_AffinePoint::identity(group);
161 } else {
162 return {};
163 }
164 case 0x02:
165 case 0x03:
166 return EC_AffinePoint::deserialize_compressed(group, bytes);
167 case 0x04:
168 return EC_AffinePoint::deserialize_uncompressed(group, bytes);
169 case 0x06:
170 case 0x07: {
171 // The deprecated "hybrid" point format
172 // TODO(Botan4) remove this
173 const bool hdr_y_is_even = bytes[0] == 0x06;
174 const bool y_is_even = (bytes.back() & 0x01) == 0;
175
176 if(hdr_y_is_even == y_is_even) {
177 std::vector<uint8_t> sec1(bytes.begin(), bytes.end());
178 sec1[0] = 0x04;
180 } else {
181 return {};
182 }
183 }
184 default:
185 return {};
186 }
187}
188
189std::optional<EC_AffinePoint> EC_AffinePoint::deserialize_compressed(const EC_Group& group,
190 std::span<const uint8_t> bytes) {
191 if(auto pt = group._data()->point_deserialize_compressed(bytes)) {
192 return EC_AffinePoint(std::move(pt));
193 } else {
194 return {};
195 }
196}
197
198std::optional<EC_AffinePoint> EC_AffinePoint::deserialize_uncompressed(const EC_Group& group,
199 std::span<const uint8_t> bytes) {
200 if(auto pt = group._data()->point_deserialize_uncompressed(bytes)) {
201 return EC_AffinePoint(std::move(pt));
202 } else {
203 return {};
204 }
205}
206
208 auto pt = scalar._inner().group()->point_g_mul(scalar.inner(), rng);
209 return EC_AffinePoint(std::move(pt));
210}
211
213 return EC_AffinePoint(inner().mul(scalar._inner(), rng));
214}
215
217 return inner().mul_x_only(scalar._inner(), rng);
218}
219
220std::optional<EC_AffinePoint> EC_AffinePoint::mul_px_qy(const EC_AffinePoint& p,
221 const EC_Scalar& x,
222 const EC_AffinePoint& q,
223 const EC_Scalar& y,
225 auto pt = p._inner().group()->mul_px_qy(p._inner(), x._inner(), q._inner(), y._inner(), rng);
226 if(pt) {
227 return EC_AffinePoint(std::move(pt));
228 } else {
229 return {};
230 }
231}
232
234 auto pt = _inner().group()->affine_add(_inner(), q._inner());
235 return EC_AffinePoint(std::move(pt));
236}
237
239 auto pt = this->_inner().group()->affine_neg(this->_inner());
240 return EC_AffinePoint(std::move(pt));
241}
242
243std::vector<uint8_t> EC_AffinePoint::serialize(EC_Point_Format format) const {
244 if(format == EC_Point_Format::Compressed) {
245 return this->serialize_compressed();
246 } else if(format == EC_Point_Format::Uncompressed) {
247 return this->serialize_uncompressed();
248 } else {
249 // The deprecated "hybrid" point encoding
250 // TODO(Botan4) Remove this
251 auto enc = this->serialize_uncompressed();
252 const bool y_is_odd = (enc[enc.size() - 1] & 0x01) == 0x01;
253 enc.front() = y_is_odd ? 0x07 : 0x06;
254 return enc;
255 }
256}
257
258void EC_AffinePoint::serialize_x_to(std::span<uint8_t> bytes) const {
260 m_point->serialize_x_to(bytes);
261}
262
263void EC_AffinePoint::serialize_y_to(std::span<uint8_t> bytes) const {
265 m_point->serialize_y_to(bytes);
266}
267
268void EC_AffinePoint::serialize_xy_to(std::span<uint8_t> bytes) const {
270 m_point->serialize_xy_to(bytes);
271}
272
273void EC_AffinePoint::serialize_compressed_to(std::span<uint8_t> bytes) const {
275 m_point->serialize_compressed_to(bytes);
276}
277
278void EC_AffinePoint::serialize_uncompressed_to(std::span<uint8_t> bytes) const {
280 m_point->serialize_uncompressed_to(bytes);
281}
282
283EC_AffinePoint EC_AffinePoint::_from_inner(std::unique_ptr<EC_AffinePoint_Data> inner) {
284 return EC_AffinePoint(std::move(inner));
285}
286
287const std::shared_ptr<const EC_Group_Data>& EC_AffinePoint::_group() const {
288 return inner().group();
289}
290
291} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:114
int signum() const
Definition bigint.h:493
void serialize_to(std::span< uint8_t > out) const
Definition bigint.cpp:395
virtual std::unique_ptr< EC_AffinePoint_Data > clone() const =0
virtual const std::shared_ptr< const EC_Group_Data > & group() const =0
static EC_AffinePoint hash_to_curve_ro(const EC_Group &group, std::string_view hash_fn, std::span< const uint8_t > input, std::span< const uint8_t > domain_sep)
EC_AffinePoint negate() const
Point negation.
size_t field_element_bytes() const
void serialize_xy_to(std::span< uint8_t > bytes) const
static EC_AffinePoint hash_to_curve_nu(const EC_Group &group, std::string_view hash_fn, std::span< const uint8_t > input, std::span< const uint8_t > domain_sep)
bool is_identity() const
Return true if this point is the identity element.
static std::optional< EC_AffinePoint > deserialize_uncompressed(const EC_Group &group, std::span< const uint8_t > bytes)
static EC_AffinePoint identity(const EC_Group &group)
Return the identity element.
Definition ec_apoint.cpp:80
static std::optional< EC_AffinePoint > from_bigint_xy(const EC_Group &group, const BigInt &x, const BigInt &y)
Definition ec_apoint.cpp:93
static std::optional< EC_AffinePoint > mul_px_qy(const EC_AffinePoint &p, const EC_Scalar &x, const EC_AffinePoint &q, const EC_Scalar &y, RandomNumberGenerator &rng)
static EC_AffinePoint g_mul(const EC_Scalar &scalar, RandomNumberGenerator &rng)
Multiply by the group generator returning a complete point.
T serialize_uncompressed() const
Definition ec_apoint.h:232
EC_AffinePoint(const EC_Group &group, std::span< const uint8_t > bytes)
Definition ec_apoint.cpp:36
EC_AffinePoint mul(const EC_Scalar &scalar, RandomNumberGenerator &rng) const
Multiply a point by a scalar returning a complete point.
static EC_AffinePoint _from_inner(std::unique_ptr< EC_AffinePoint_Data > inner)
const std::shared_ptr< const EC_Group_Data > & _group() const
static std::optional< EC_AffinePoint > deserialize(const EC_Group &group, std::span< const uint8_t > bytes)
static std::optional< EC_AffinePoint > deserialize_compressed(const EC_Group &group, std::span< const uint8_t > bytes)
std::vector< uint8_t > serialize(EC_Point_Format format) const
Return an encoding depending on the requested format.
void serialize_x_to(std::span< uint8_t > bytes) const
T serialize_compressed() const
Definition ec_apoint.h:242
void serialize_compressed_to(std::span< uint8_t > bytes) const
secure_vector< uint8_t > mul_x_only(const EC_Scalar &scalar, RandomNumberGenerator &rng) const
Multiply a point by a scalar, returning the byte encoding of the x coordinate only.
const EC_AffinePoint_Data & _inner() const
Definition ec_apoint.h:337
void serialize_uncompressed_to(std::span< uint8_t > bytes) const
void serialize_y_to(std::span< uint8_t > bytes) const
EC_AffinePoint add(const EC_AffinePoint &q) const
EC_AffinePoint & operator=(const EC_AffinePoint &other)
Definition ec_apoint.cpp:24
bool operator==(const EC_AffinePoint &other) const
Definition ec_apoint.cpp:55
static EC_AffinePoint generator(const EC_Group &group)
Return the standard group generator.
Definition ec_apoint.cpp:84
const BigInt & get_g_y() const
Definition ec_group.cpp:726
const BigInt & get_p() const
Definition ec_group.cpp:670
const BigInt & get_g_x() const
Definition ec_group.cpp:722
const std::shared_ptr< EC_Group_Data > & _data() const
Definition ec_group.h:545
size_t get_p_bytes() const
Definition ec_group.cpp:658
virtual const std::shared_ptr< const EC_Group_Data > & group() const =0
const EC_Scalar_Data & _inner() const
Definition ec_scalar.h:277
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:798
std::span< const uint8_t > as_span_of_bytes(const char *s, size_t len)
Definition mem_utils.h:59
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128