Botan 3.6.0
Crypto and TLS for C&
ec_inner_bn.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/internal/ec_inner_bn.h>
8
9namespace Botan {
10
12 const auto* p = dynamic_cast<const EC_Scalar_Data_BN*>(&data);
13 if(!p) {
14 throw Invalid_State("Failed conversion to EC_Scalar_Data_BN");
15 }
16 return *p;
17}
18
19const std::shared_ptr<const EC_Group_Data>& EC_Scalar_Data_BN::group() const {
20 return m_group;
21}
22
24 return this->group()->order_bytes();
25}
26
27std::unique_ptr<EC_Scalar_Data> EC_Scalar_Data_BN::clone() const {
28 return std::make_unique<EC_Scalar_Data_BN>(this->group(), this->value());
29}
30
32 return this->value().is_zero();
33}
34
35bool EC_Scalar_Data_BN::is_eq(const EC_Scalar_Data& other) const {
36 return (value() == checked_ref(other).value());
37}
38
40 m_v = checked_ref(other).value();
41}
42
44 m_group->square_mod_order(m_v);
45}
46
47std::unique_ptr<EC_Scalar_Data> EC_Scalar_Data_BN::negate() const {
48 return std::make_unique<EC_Scalar_Data_BN>(m_group, m_group->mod_order(-m_v));
49}
50
51std::unique_ptr<EC_Scalar_Data> EC_Scalar_Data_BN::invert() const {
52 return std::make_unique<EC_Scalar_Data_BN>(m_group, m_group->inverse_mod_order(m_v));
53}
54
55std::unique_ptr<EC_Scalar_Data> EC_Scalar_Data_BN::add(const EC_Scalar_Data& other) const {
56 return std::make_unique<EC_Scalar_Data_BN>(m_group, m_group->mod_order(m_v + checked_ref(other).value()));
57}
58
59std::unique_ptr<EC_Scalar_Data> EC_Scalar_Data_BN::sub(const EC_Scalar_Data& other) const {
60 return std::make_unique<EC_Scalar_Data_BN>(m_group, m_group->mod_order(m_v - checked_ref(other).value()));
61}
62
63std::unique_ptr<EC_Scalar_Data> EC_Scalar_Data_BN::mul(const EC_Scalar_Data& other) const {
64 return std::make_unique<EC_Scalar_Data_BN>(m_group, m_group->multiply_mod_order(m_v, checked_ref(other).value()));
65}
66
67void EC_Scalar_Data_BN::serialize_to(std::span<uint8_t> bytes) const {
68 BOTAN_ARG_CHECK(bytes.size() == m_group->order_bytes(), "Invalid output length");
70}
71
72EC_AffinePoint_Data_BN::EC_AffinePoint_Data_BN(std::shared_ptr<const EC_Group_Data> group, EC_Point pt) :
73 m_group(std::move(group)), m_pt(std::move(pt)) {
74 if(!m_pt.is_zero()) {
75 m_pt.force_affine();
76 m_xy = m_pt.xy_bytes();
77 }
78}
79
80EC_AffinePoint_Data_BN::EC_AffinePoint_Data_BN(std::shared_ptr<const EC_Group_Data> group,
81 std::span<const uint8_t> pt) :
82 m_group(std::move(group)) {
83 BOTAN_ASSERT_NONNULL(m_group);
84 m_pt = Botan::OS2ECP(pt.data(), pt.size(), m_group->curve());
85 if(!m_pt.is_zero()) {
86 m_xy = m_pt.xy_bytes();
87 }
88}
89
90std::unique_ptr<EC_AffinePoint_Data> EC_AffinePoint_Data_BN::clone() const {
91 return std::make_unique<EC_AffinePoint_Data_BN>(m_group, m_pt);
92}
93
94const std::shared_ptr<const EC_Group_Data>& EC_AffinePoint_Data_BN::group() const {
95 return m_group;
96}
97
98std::unique_ptr<EC_AffinePoint_Data> EC_AffinePoint_Data_BN::mul(const EC_Scalar_Data& scalar,
100 std::vector<BigInt>& ws) const {
101 BOTAN_ARG_CHECK(scalar.group() == m_group, "Curve mismatch");
102 const auto& bn = EC_Scalar_Data_BN::checked_ref(scalar);
103
104 EC_Point_Var_Point_Precompute mul(m_pt, rng, ws);
105
106 // We pass order*cofactor here to "correctly" handle the case where the
107 // point is on the curve but not in the prime order subgroup. This only
108 // matters for groups with cofactor > 1
109 // See https://github.com/randombit/botan/issues/3800
110
111 const auto order = m_group->order() * m_group->cofactor();
112 auto pt = mul.mul(bn.value(), rng, order, ws);
113 return std::make_unique<EC_AffinePoint_Data_BN>(m_group, std::move(pt));
114}
115
117 return m_group->p_bytes();
118}
119
121 return m_xy.empty();
122}
123
124void EC_AffinePoint_Data_BN::serialize_x_to(std::span<uint8_t> bytes) const {
126 const size_t fe_bytes = this->field_element_bytes();
127 BOTAN_ARG_CHECK(bytes.size() == fe_bytes, "Invalid output size");
128 copy_mem(bytes, std::span{m_xy}.first(fe_bytes));
129}
130
131void EC_AffinePoint_Data_BN::serialize_y_to(std::span<uint8_t> bytes) const {
133 const size_t fe_bytes = this->field_element_bytes();
134 BOTAN_ARG_CHECK(bytes.size() == fe_bytes, "Invalid output size");
135 copy_mem(bytes, std::span{m_xy}.last(fe_bytes));
136}
137
138void EC_AffinePoint_Data_BN::serialize_xy_to(std::span<uint8_t> bytes) const {
140 const size_t fe_bytes = this->field_element_bytes();
141 BOTAN_ARG_CHECK(bytes.size() == 2 * fe_bytes, "Invalid output size");
142 copy_mem(bytes, m_xy);
143}
144
145void EC_AffinePoint_Data_BN::serialize_compressed_to(std::span<uint8_t> bytes) const {
147 const size_t fe_bytes = this->field_element_bytes();
148 BOTAN_ARG_CHECK(bytes.size() == 1 + fe_bytes, "Invalid output size");
149 const bool y_is_odd = (m_xy[m_xy.size() - 1] & 0x01) == 0x01;
150
151 BufferStuffer stuffer(bytes);
152 stuffer.append(y_is_odd ? 0x03 : 0x02);
153 serialize_x_to(stuffer.next(fe_bytes));
154}
155
156void EC_AffinePoint_Data_BN::serialize_uncompressed_to(std::span<uint8_t> bytes) const {
158 const size_t fe_bytes = this->field_element_bytes();
159 BOTAN_ARG_CHECK(bytes.size() == 1 + 2 * fe_bytes, "Invalid output size");
160 BufferStuffer stuffer(bytes);
161 stuffer.append(0x04);
162 stuffer.append(m_xy);
163}
164
166 m_group(g.group()), m_tbl(g.to_legacy_point(), h.to_legacy_point()) {
167 BOTAN_ARG_CHECK(h.group() == m_group, "Curve mismatch");
168}
169
170std::unique_ptr<EC_AffinePoint_Data> EC_Mul2Table_Data_BN::mul2_vartime(const EC_Scalar_Data& x,
171 const EC_Scalar_Data& y) const {
172 BOTAN_ARG_CHECK(x.group() == m_group && y.group() == m_group, "Curve mismatch");
173
174 const auto& bn_x = EC_Scalar_Data_BN::checked_ref(x);
175 const auto& bn_y = EC_Scalar_Data_BN::checked_ref(y);
176 auto pt = m_tbl.multi_exp(bn_x.value(), bn_y.value());
177
178 if(pt.is_zero()) {
179 return nullptr;
180 }
181 return std::make_unique<EC_AffinePoint_Data_BN>(m_group, std::move(pt));
182}
183
185 const EC_Scalar_Data& x,
186 const EC_Scalar_Data& y) const {
187 BOTAN_ARG_CHECK(x.group() == m_group && y.group() == m_group && v.group() == m_group, "Curve mismatch");
188
189 const auto& bn_v = EC_Scalar_Data_BN::checked_ref(v);
190 const auto& bn_x = EC_Scalar_Data_BN::checked_ref(x);
191 const auto& bn_y = EC_Scalar_Data_BN::checked_ref(y);
192 const auto pt = m_tbl.multi_exp(bn_x.value(), bn_y.value());
193
194 if(pt.is_zero()) {
195 return false;
196 }
197
198 /*
199 * The trick used below doesn't work for curves with cofactors
200 */
201 if(m_group->has_cofactor()) {
202 return m_group->mod_order(pt.get_affine_x()) == bn_v.value();
203 }
204
205 /*
206 * Note we're working with the projective coordinate directly here!
207 * Nominally we're comparing v with the affine x coordinate.
208 *
209 * return m_group->mod_order(pt.get_affine_x()) == bn_v.value();
210 *
211 * However by instead projecting r to an identical z as the x
212 * coordinate, we can compare without having to perform an
213 * expensive inversion in the field.
214 *
215 * That is, given (x*z2) and r, instead of checking if
216 * (x*z2)*z2^-1 == r,
217 * we check if
218 * (x*z2) == (r*z2)
219 */
220 auto& curve = m_group->curve();
221
223 BigInt vr = bn_v.value();
224 curve.to_rep(vr, ws);
225 BigInt z2, v_z2;
226 curve.sqr(z2, pt.get_z(), ws);
227 curve.mul(v_z2, vr, z2, ws);
228
229 /*
230 * Since (typically) the group order is slightly less than the size
231 * of the field elements, its possible the signer had to reduce the
232 * r component. If they did not reduce r, then this value is correct.
233 *
234 * Due to the Hasse bound, this case occurs almost always; the
235 * probability that a reduction was actually required is
236 * approximately 1 in 2^(n/2) where n is the bit length of the curve.
237 */
238 if(pt.get_x() == v_z2) {
239 return true;
240 }
241
242 if(m_group->order_is_less_than_p()) {
243 vr = bn_v.value() + m_group->order();
244 if(vr < m_group->p()) {
245 curve.to_rep(vr, ws);
246 curve.mul(v_z2, vr, z2, ws);
247
248 if(pt.get_x() == v_z2) {
249 return true;
250 }
251 }
252 }
253
254 // Reject:
255 return false;
256}
257
258} // namespace Botan
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:86
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
BigInt & mul(const BigInt &y, secure_vector< word > &ws)
Definition big_ops2.cpp:156
void serialize_to(std::span< uint8_t > out) const
Definition bigint.cpp:383
bool is_zero() const
Definition bigint.h:458
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:142
constexpr void append(std::span< const uint8_t > buffer)
Definition stl_util.h:177
constexpr std::span< uint8_t > next(size_t bytes)
Definition stl_util.h:150
void serialize_x_to(std::span< uint8_t > bytes) const override
const std::shared_ptr< const EC_Group_Data > & group() const override
EC_AffinePoint_Data_BN(std::shared_ptr< const EC_Group_Data > group, EC_Point pt)
void serialize_compressed_to(std::span< uint8_t > bytes) const override
std::unique_ptr< EC_AffinePoint_Data > mul(const EC_Scalar_Data &scalar, RandomNumberGenerator &rng, std::vector< BigInt > &ws) const override
std::unique_ptr< EC_AffinePoint_Data > clone() const override
size_t field_element_bytes() const override
bool is_identity() const override
void serialize_y_to(std::span< uint8_t > bytes) const override
void serialize_xy_to(std::span< uint8_t > bytes) const override
void serialize_uncompressed_to(std::span< uint8_t > bytes) const override
virtual const std::shared_ptr< const EC_Group_Data > & group() const =0
EC_Mul2Table_Data_BN(const EC_AffinePoint_Data &g, const EC_AffinePoint_Data &h)
bool mul2_vartime_x_mod_order_eq(const EC_Scalar_Data &v, const EC_Scalar_Data &x, const EC_Scalar_Data &y) const override
std::unique_ptr< EC_AffinePoint_Data > mul2_vartime(const EC_Scalar_Data &x, const EC_Scalar_Data &y) const override
EC_Point multi_exp(const BigInt &k1, const BigInt &k2) const
secure_vector< uint8_t > xy_bytes() const
Definition ec_point.cpp:482
bool is_zero() const
Definition ec_point.h:167
std::unique_ptr< EC_Scalar_Data > invert() const override
std::unique_ptr< EC_Scalar_Data > clone() const override
std::unique_ptr< EC_Scalar_Data > negate() const override
const std::shared_ptr< const EC_Group_Data > & group() const override
bool is_zero() const override
std::unique_ptr< EC_Scalar_Data > sub(const EC_Scalar_Data &other) const override
void square_self() override
std::unique_ptr< EC_Scalar_Data > add(const EC_Scalar_Data &other) const override
std::unique_ptr< EC_Scalar_Data > mul(const EC_Scalar_Data &other) const override
const BigInt & value() const
Definition ec_inner_bn.h:48
void serialize_to(std::span< uint8_t > bytes) const override
static const EC_Scalar_Data_BN & checked_ref(const EC_Scalar_Data &data)
bool is_eq(const EC_Scalar_Data &y) const override
void assign(const EC_Scalar_Data &y) override
size_t bytes() const override
virtual const std::shared_ptr< const EC_Group_Data > & group() const =0
EC_Point OS2ECP(const uint8_t data[], size_t data_len, const CurveGFp &curve)
Definition ec_point.cpp:648
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:146