/src/botan/src/lib/pubkey/ecdsa/ecdsa.cpp
Line | Count | Source |
1 | | /* |
2 | | * ECDSA implementation |
3 | | * (C) 2007 Manuel Hartl, FlexSecure GmbH |
4 | | * 2007 Falko Strenzke, FlexSecure GmbH |
5 | | * 2008-2010,2015,2016,2018,2024 Jack Lloyd |
6 | | * 2016 René Korthaus |
7 | | * |
8 | | * Botan is released under the Simplified BSD License (see license.txt) |
9 | | */ |
10 | | |
11 | | #include <botan/ecdsa.h> |
12 | | |
13 | | #include <botan/internal/keypair.h> |
14 | | #include <botan/internal/pk_ops_impl.h> |
15 | | |
16 | | #if defined(BOTAN_HAS_RFC6979_GENERATOR) |
17 | | #include <botan/internal/rfc6979.h> |
18 | | #endif |
19 | | |
20 | | namespace Botan { |
21 | | |
22 | | namespace { |
23 | | |
24 | | EC_AffinePoint recover_ecdsa_public_key( |
25 | 0 | const EC_Group& group, const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s, uint8_t v) { |
26 | 0 | if(group.has_cofactor()) { |
27 | 0 | throw Invalid_Argument("ECDSA public key recovery only supported for prime order groups"); |
28 | 0 | } |
29 | | |
30 | 0 | if(v >= 4) { |
31 | 0 | throw Invalid_Argument("Unexpected v param for ECDSA public key recovery"); |
32 | 0 | } |
33 | | |
34 | 0 | const BigInt& group_order = group.get_order(); |
35 | |
|
36 | 0 | if(r <= 0 || r >= group_order || s <= 0 || s >= group_order) { |
37 | 0 | throw Invalid_Argument("Out of range r/s cannot recover ECDSA public key"); |
38 | 0 | } |
39 | | |
40 | 0 | const uint8_t y_odd = v % 2; |
41 | 0 | const bool add_order = (v >> 1) == 0x01; |
42 | 0 | const size_t p_bytes = group.get_p_bytes(); |
43 | |
|
44 | 0 | BigInt x = r; |
45 | |
|
46 | 0 | if(add_order) { |
47 | 0 | x += group_order; |
48 | 0 | } |
49 | |
|
50 | 0 | if(x.bytes() <= p_bytes) { |
51 | 0 | std::vector<uint8_t> X(p_bytes + 1); |
52 | |
|
53 | 0 | X[0] = 0x02 | y_odd; |
54 | 0 | x.serialize_to(std::span{X}.subspan(1)); |
55 | |
|
56 | 0 | if(auto R = EC_AffinePoint::deserialize(group, X)) { |
57 | | // Compute r_inv * (-eG + s*R) |
58 | 0 | const auto ne = EC_Scalar::from_bytes_with_trunc(group, msg).negate(); |
59 | 0 | const auto ss = EC_Scalar::from_bigint(group, s); |
60 | |
|
61 | 0 | const auto r_inv = EC_Scalar::from_bigint(group, r).invert_vartime(); |
62 | |
|
63 | 0 | EC_Group::Mul2Table GR_mul(R.value()); |
64 | 0 | if(auto egsr = GR_mul.mul2_vartime(ne * r_inv, ss * r_inv)) { |
65 | 0 | return egsr.value(); |
66 | 0 | } |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | 0 | throw Decoding_Error("Failed to recover ECDSA public key from signature/msg pair"); |
71 | 0 | } |
72 | | |
73 | | } // namespace |
74 | | |
75 | | ECDSA_PublicKey::ECDSA_PublicKey( |
76 | | const EC_Group& group, const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s, uint8_t v) : |
77 | 0 | EC_PublicKey(group, recover_ecdsa_public_key(group, msg, r, s, v)) {} Unexecuted instantiation: Botan::ECDSA_PublicKey::ECDSA_PublicKey(Botan::EC_Group const&, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > const&, Botan::BigInt const&, Botan::BigInt const&, unsigned char) Unexecuted instantiation: Botan::ECDSA_PublicKey::ECDSA_PublicKey(Botan::EC_Group const&, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > const&, Botan::BigInt const&, Botan::BigInt const&, unsigned char) |
78 | | |
79 | 0 | std::unique_ptr<Private_Key> ECDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const { |
80 | 0 | return std::make_unique<ECDSA_PrivateKey>(rng, domain()); |
81 | 0 | } |
82 | | |
83 | 0 | uint8_t ECDSA_PublicKey::recovery_param(const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s) const { |
84 | 0 | const auto this_key = this->_public_ec_point().serialize_compressed(); |
85 | |
|
86 | 0 | for(uint8_t v = 0; v != 4; ++v) { |
87 | 0 | try { |
88 | 0 | const auto R = recover_ecdsa_public_key(this->domain(), msg, r, s, v); |
89 | |
|
90 | 0 | if(R.serialize_compressed() == this_key) { |
91 | 0 | return v; |
92 | 0 | } |
93 | 0 | } catch(Decoding_Error&) { |
94 | | // try the next v |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | 0 | throw Internal_Error("Could not determine ECDSA recovery parameter"); |
99 | 0 | } |
100 | | |
101 | 0 | std::unique_ptr<Public_Key> ECDSA_PrivateKey::public_key() const { |
102 | 0 | return std::make_unique<ECDSA_PublicKey>(domain(), _public_ec_point()); |
103 | 0 | } |
104 | | |
105 | 0 | bool ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { |
106 | 0 | if(!EC_PrivateKey::check_key(rng, strong)) { |
107 | 0 | return false; |
108 | 0 | } |
109 | | |
110 | 0 | if(!strong) { |
111 | 0 | return true; |
112 | 0 | } |
113 | | |
114 | 0 | return KeyPair::signature_consistency_check(rng, *this, "SHA-256"); |
115 | 0 | } |
116 | | |
117 | | namespace { |
118 | | |
119 | | /** |
120 | | * ECDSA signature operation |
121 | | */ |
122 | | class ECDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { |
123 | | public: |
124 | | ECDSA_Signature_Operation(const ECDSA_PrivateKey& ecdsa, std::string_view padding, RandomNumberGenerator& rng) : |
125 | 0 | PK_Ops::Signature_with_Hash(padding), |
126 | 0 | m_group(ecdsa.domain()), |
127 | 0 | m_x(ecdsa._private_key()), |
128 | 0 | m_b(EC_Scalar::random(m_group, rng)), |
129 | 0 | m_b_inv(m_b.invert()) { |
130 | 0 | #if defined(BOTAN_HAS_RFC6979_GENERATOR) |
131 | 0 | m_rfc6979 = std::make_unique<RFC6979_Nonce_Generator>( |
132 | 0 | this->rfc6979_hash_function(), m_group.get_order_bits(), ecdsa._private_key()); |
133 | 0 | #endif |
134 | 0 | } |
135 | | |
136 | 0 | size_t signature_length() const override { return 2 * m_group.get_order_bytes(); } |
137 | | |
138 | | std::vector<uint8_t> raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) override; |
139 | | |
140 | | AlgorithmIdentifier algorithm_identifier() const override; |
141 | | |
142 | | private: |
143 | | const EC_Group m_group; |
144 | | const EC_Scalar m_x; |
145 | | |
146 | | #if defined(BOTAN_HAS_RFC6979_GENERATOR) |
147 | | std::unique_ptr<RFC6979_Nonce_Generator> m_rfc6979; |
148 | | #endif |
149 | | |
150 | | EC_Scalar m_b; |
151 | | EC_Scalar m_b_inv; |
152 | | }; |
153 | | |
154 | 0 | AlgorithmIdentifier ECDSA_Signature_Operation::algorithm_identifier() const { |
155 | 0 | const std::string full_name = "ECDSA/" + hash_function(); |
156 | 0 | const OID oid = OID::from_string(full_name); |
157 | 0 | return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM); |
158 | 0 | } |
159 | | |
160 | 0 | std::vector<uint8_t> ECDSA_Signature_Operation::raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) { |
161 | 0 | const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg); |
162 | |
|
163 | 0 | #if defined(BOTAN_HAS_RFC6979_GENERATOR) |
164 | 0 | const auto k = m_rfc6979->nonce_for(m_group, m); |
165 | | #else |
166 | | const auto k = EC_Scalar::random(m_group, rng); |
167 | | #endif |
168 | |
|
169 | 0 | const auto r = EC_Scalar::gk_x_mod_order(k, rng); |
170 | | |
171 | | // Blind the inversion of k |
172 | 0 | const auto k_inv = (m_b * k).invert() * m_b; |
173 | | |
174 | | /* |
175 | | * Blind the input message and compute x*r+m as (x*r*b + m*b)/b |
176 | | */ |
177 | 0 | m_b.square_self(); |
178 | 0 | m_b_inv.square_self(); |
179 | |
|
180 | 0 | const auto xr_m = ((m_x * m_b) * r) + (m * m_b); |
181 | |
|
182 | 0 | const auto s = (k_inv * xr_m) * m_b_inv; |
183 | | |
184 | | // With overwhelming probability, a bug rather than actual zero r/s |
185 | 0 | if(r.is_zero() || s.is_zero()) { |
186 | 0 | throw Internal_Error("During ECDSA signature generated zero r/s"); |
187 | 0 | } |
188 | | |
189 | 0 | return EC_Scalar::serialize_pair(r, s); |
190 | 0 | } |
191 | | |
192 | | /** |
193 | | * ECDSA verification operation |
194 | | */ |
195 | | class ECDSA_Verification_Operation final : public PK_Ops::Verification_with_Hash { |
196 | | public: |
197 | | ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, std::string_view padding) : |
198 | 0 | PK_Ops::Verification_with_Hash(padding), m_group(ecdsa.domain()), m_gy_mul(ecdsa._public_ec_point()) {} |
199 | | |
200 | | ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, const AlgorithmIdentifier& alg_id) : |
201 | 2.16k | PK_Ops::Verification_with_Hash(alg_id, "ECDSA", true), |
202 | 2.16k | m_group(ecdsa.domain()), |
203 | 2.16k | m_gy_mul(ecdsa._public_ec_point()) {} |
204 | | |
205 | | bool verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) override; |
206 | | |
207 | | private: |
208 | | const EC_Group m_group; |
209 | | const EC_Group::Mul2Table m_gy_mul; |
210 | | }; |
211 | | |
212 | 1.28k | bool ECDSA_Verification_Operation::verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) { |
213 | 1.28k | if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) { |
214 | 1.17k | const auto& [r, s] = rs.value(); |
215 | | |
216 | 1.17k | if(r.is_nonzero() && s.is_nonzero()) { |
217 | 1.17k | const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg); |
218 | | |
219 | 1.17k | const auto w = s.invert_vartime(); |
220 | | |
221 | | // Check if r == x_coord(g*w*m + y*w*r) % n |
222 | 1.17k | return m_gy_mul.mul2_vartime_x_mod_order_eq(r, w, m, r); |
223 | 1.17k | } |
224 | 1.17k | } |
225 | | |
226 | 102 | return false; |
227 | 1.28k | } |
228 | | |
229 | | } // namespace |
230 | | |
231 | | std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_verification_op(std::string_view params, |
232 | 0 | std::string_view provider) const { |
233 | 0 | if(provider == "base" || provider.empty()) { |
234 | 0 | return std::make_unique<ECDSA_Verification_Operation>(*this, params); |
235 | 0 | } |
236 | | |
237 | 0 | throw Provider_Not_Found(algo_name(), provider); |
238 | 0 | } |
239 | | |
240 | | std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_x509_verification_op( |
241 | 2.16k | const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const { |
242 | 2.16k | if(provider == "base" || provider.empty()) { |
243 | 2.16k | return std::make_unique<ECDSA_Verification_Operation>(*this, signature_algorithm); |
244 | 2.16k | } |
245 | | |
246 | 0 | throw Provider_Not_Found(algo_name(), provider); |
247 | 2.16k | } |
248 | | |
249 | | std::unique_ptr<PK_Ops::Signature> ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng, |
250 | | std::string_view params, |
251 | 0 | std::string_view provider) const { |
252 | 0 | if(provider == "base" || provider.empty()) { |
253 | 0 | return std::make_unique<ECDSA_Signature_Operation>(*this, params, rng); |
254 | 0 | } |
255 | | |
256 | 0 | throw Provider_Not_Found(algo_name(), provider); |
257 | 0 | } |
258 | | |
259 | | } // namespace Botan |