/src/botan/src/lib/pubkey/ecdsa/ecdsa.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ECDSA implemenation |
3 | | * (C) 2007 Manuel Hartl, FlexSecure GmbH |
4 | | * 2007 Falko Strenzke, FlexSecure GmbH |
5 | | * 2008-2010,2015,2016,2018 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/reducer.h> |
14 | | #include <botan/internal/keypair.h> |
15 | | #include <botan/internal/pk_ops_impl.h> |
16 | | #include <botan/internal/point_mul.h> |
17 | | |
18 | | #if defined(BOTAN_HAS_RFC6979_GENERATOR) |
19 | | #include <botan/internal/rfc6979.h> |
20 | | #endif |
21 | | |
22 | | namespace Botan { |
23 | | |
24 | | namespace { |
25 | | |
26 | | EC_Point recover_ecdsa_public_key( |
27 | 0 | const EC_Group& group, const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s, uint8_t v) { |
28 | 0 | if(group.get_cofactor() != 1) { |
29 | 0 | throw Invalid_Argument("ECDSA public key recovery only supported for prime order groups"); |
30 | 0 | } |
31 | | |
32 | 0 | if(v >= 4) { |
33 | 0 | throw Invalid_Argument("Unexpected v param for ECDSA public key recovery"); |
34 | 0 | } |
35 | | |
36 | 0 | const BigInt& group_order = group.get_order(); |
37 | |
|
38 | 0 | if(r <= 0 || r >= group_order || s <= 0 || s >= group_order) { |
39 | 0 | throw Invalid_Argument("Out of range r/s cannot recover ECDSA public key"); |
40 | 0 | } |
41 | | |
42 | 0 | const uint8_t y_odd = v % 2; |
43 | 0 | const uint8_t add_order = v >> 1; |
44 | 0 | const size_t p_bytes = group.get_p_bytes(); |
45 | |
|
46 | 0 | try { |
47 | 0 | const BigInt e = BigInt::from_bytes_with_max_bits(msg.data(), msg.size(), group.get_order_bits()); |
48 | 0 | const BigInt r_inv = group.inverse_mod_order(r); |
49 | |
|
50 | 0 | BigInt x = r + add_order * group_order; |
51 | |
|
52 | 0 | std::vector<uint8_t> X(p_bytes + 1); |
53 | |
|
54 | 0 | X[0] = 0x02 | y_odd; |
55 | 0 | x.serialize_to(std::span{X}.subspan(1)); |
56 | |
|
57 | 0 | const EC_Point R = group.OS2ECP(X); |
58 | |
|
59 | 0 | if((R * group_order).is_zero() == false) { |
60 | 0 | throw Decoding_Error("Unable to recover ECDSA public key"); |
61 | 0 | } |
62 | | |
63 | | // Compute r_inv * (s*R - eG) |
64 | 0 | EC_Point_Multi_Point_Precompute RG_mul(R, group.get_base_point()); |
65 | 0 | const BigInt ne = group.mod_order(group_order - e); |
66 | 0 | return r_inv * RG_mul.multi_exp(s, ne); |
67 | 0 | } catch(...) { |
68 | | // continue on and throw |
69 | 0 | } |
70 | | |
71 | 0 | throw Decoding_Error("Failed to recover ECDSA public key from signature/msg pair"); |
72 | 0 | } |
73 | | |
74 | | } // namespace |
75 | | |
76 | | ECDSA_PublicKey::ECDSA_PublicKey( |
77 | | const EC_Group& group, const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s, uint8_t v) : |
78 | 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) |
79 | | |
80 | 0 | std::unique_ptr<Private_Key> ECDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const { |
81 | 0 | return std::make_unique<ECDSA_PrivateKey>(rng, domain()); |
82 | 0 | } |
83 | | |
84 | 0 | uint8_t ECDSA_PublicKey::recovery_param(const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s) const { |
85 | 0 | for(uint8_t v = 0; v != 4; ++v) { |
86 | 0 | try { |
87 | 0 | EC_Point R = recover_ecdsa_public_key(this->domain(), msg, r, s, v); |
88 | |
|
89 | 0 | if(R == this->public_point()) { |
90 | 0 | return v; |
91 | 0 | } |
92 | 0 | } catch(Decoding_Error&) { |
93 | | // try the next v |
94 | 0 | } |
95 | 0 | } |
96 | | |
97 | 0 | throw Internal_Error("Could not determine ECDSA recovery parameter"); |
98 | 0 | } |
99 | | |
100 | 0 | std::unique_ptr<Public_Key> ECDSA_PrivateKey::public_key() const { |
101 | 0 | return std::make_unique<ECDSA_PublicKey>(domain(), public_point()); |
102 | 0 | } |
103 | | |
104 | 0 | bool ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const { |
105 | 0 | if(!EC_PrivateKey::check_key(rng, strong)) { |
106 | 0 | return false; |
107 | 0 | } |
108 | | |
109 | 0 | if(!strong) { |
110 | 0 | return true; |
111 | 0 | } |
112 | | |
113 | 0 | return KeyPair::signature_consistency_check(rng, *this, "SHA-256"); |
114 | 0 | } |
115 | | |
116 | | namespace { |
117 | | |
118 | | /** |
119 | | * ECDSA signature operation |
120 | | */ |
121 | | class ECDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { |
122 | | public: |
123 | | ECDSA_Signature_Operation(const ECDSA_PrivateKey& ecdsa, std::string_view padding, RandomNumberGenerator& rng) : |
124 | 93 | PK_Ops::Signature_with_Hash(padding), m_group(ecdsa.domain()), m_x(ecdsa.private_value()) { |
125 | 93 | #if defined(BOTAN_HAS_RFC6979_GENERATOR) |
126 | 93 | m_rfc6979 = std::make_unique<RFC6979_Nonce_Generator>(this->rfc6979_hash_function(), m_group.get_order(), m_x); |
127 | 93 | #endif |
128 | | |
129 | 93 | m_b = m_group.random_scalar(rng); |
130 | 93 | m_b_inv = m_group.inverse_mod_order(m_b); |
131 | 93 | } |
132 | | |
133 | 0 | size_t signature_length() const override { return 2 * m_group.get_order_bytes(); } |
134 | | |
135 | | secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng) override; |
136 | | |
137 | | AlgorithmIdentifier algorithm_identifier() const override; |
138 | | |
139 | | private: |
140 | | const EC_Group m_group; |
141 | | const BigInt m_x; |
142 | | |
143 | | #if defined(BOTAN_HAS_RFC6979_GENERATOR) |
144 | | std::unique_ptr<RFC6979_Nonce_Generator> m_rfc6979; |
145 | | #endif |
146 | | |
147 | | std::vector<BigInt> m_ws; |
148 | | |
149 | | BigInt m_b, m_b_inv; |
150 | | }; |
151 | | |
152 | 0 | AlgorithmIdentifier ECDSA_Signature_Operation::algorithm_identifier() const { |
153 | 0 | const std::string full_name = "ECDSA/" + hash_function(); |
154 | 0 | const OID oid = OID::from_string(full_name); |
155 | 0 | return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM); |
156 | 0 | } |
157 | | |
158 | | secure_vector<uint8_t> ECDSA_Signature_Operation::raw_sign(const uint8_t msg[], |
159 | | size_t msg_len, |
160 | 93 | RandomNumberGenerator& rng) { |
161 | 93 | BigInt m = m_group.mod_order(BigInt::from_bytes_with_max_bits(msg, msg_len, m_group.get_order_bits())); |
162 | | |
163 | 93 | #if defined(BOTAN_HAS_RFC6979_GENERATOR) |
164 | 93 | const BigInt k = m_rfc6979->nonce_for(m); |
165 | | #else |
166 | | const BigInt k = m_group.random_scalar(rng); |
167 | | #endif |
168 | | |
169 | 93 | const BigInt r = m_group.mod_order(m_group.blinded_base_point_multiply_x(k, rng, m_ws)); |
170 | | |
171 | 93 | const BigInt k_inv = m_group.inverse_mod_order(k); |
172 | | |
173 | | /* |
174 | | * Blind the input message and compute x*r+m as (x*r*b + m*b)/b |
175 | | */ |
176 | 93 | m_b = m_group.square_mod_order(m_b); |
177 | 93 | m_b_inv = m_group.square_mod_order(m_b_inv); |
178 | | |
179 | 93 | m = m_group.multiply_mod_order(m_b, m_group.mod_order(m)); |
180 | 93 | const BigInt xr_m = m_group.mod_order(m_group.multiply_mod_order(m_x, m_b, r) + m); |
181 | | |
182 | 93 | const BigInt s = m_group.multiply_mod_order(k_inv, xr_m, m_b_inv); |
183 | | |
184 | | // With overwhelming probability, a bug rather than actual zero r/s |
185 | 93 | if(r.is_zero() || s.is_zero()) { |
186 | 0 | throw Internal_Error("During ECDSA signature generated zero r/s"); |
187 | 0 | } |
188 | | |
189 | 93 | return BigInt::encode_fixed_length_int_pair(r, s, m_group.get_order_bytes()); |
190 | 93 | } |
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 | | PK_Ops::Verification_with_Hash(padding), |
199 | | m_group(ecdsa.domain()), |
200 | 250 | m_gy_mul(m_group.get_base_point(), ecdsa.public_point()) {} |
201 | | |
202 | | ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, const AlgorithmIdentifier& alg_id) : |
203 | | PK_Ops::Verification_with_Hash(alg_id, "ECDSA", true), |
204 | | m_group(ecdsa.domain()), |
205 | 0 | m_gy_mul(m_group.get_base_point(), ecdsa.public_point()) {} |
206 | | |
207 | | bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) override; |
208 | | |
209 | | private: |
210 | | const EC_Group m_group; |
211 | | const EC_Point_Multi_Point_Precompute m_gy_mul; |
212 | | }; |
213 | | |
214 | 250 | bool ECDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) { |
215 | 250 | if(sig_len != m_group.get_order_bytes() * 2) { |
216 | 0 | return false; |
217 | 0 | } |
218 | | |
219 | 250 | const BigInt e = BigInt::from_bytes_with_max_bits(msg, msg_len, m_group.get_order_bits()); |
220 | | |
221 | 250 | const BigInt r(sig, sig_len / 2); |
222 | 250 | const BigInt s(sig + sig_len / 2, sig_len / 2); |
223 | | |
224 | | // Cannot be negative here since we just decoded from binary |
225 | 250 | if(r.is_zero() || s.is_zero()) { |
226 | 12 | return false; |
227 | 12 | } |
228 | | |
229 | 238 | if(r >= m_group.get_order() || s >= m_group.get_order()) { |
230 | 6 | return false; |
231 | 6 | } |
232 | | |
233 | 232 | const BigInt w = m_group.inverse_mod_order(s); |
234 | | |
235 | 232 | const BigInt u1 = m_group.multiply_mod_order(m_group.mod_order(e), w); |
236 | 232 | const BigInt u2 = m_group.multiply_mod_order(r, w); |
237 | 232 | const EC_Point R = m_gy_mul.multi_exp(u1, u2); |
238 | | |
239 | 232 | if(R.is_zero()) { |
240 | 26 | return false; |
241 | 26 | } |
242 | | |
243 | 206 | const BigInt v = m_group.mod_order(R.get_affine_x()); |
244 | 206 | return (v == r); |
245 | 232 | } |
246 | | |
247 | | } // namespace |
248 | | |
249 | | std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_verification_op(std::string_view params, |
250 | 250 | std::string_view provider) const { |
251 | 250 | if(provider == "base" || provider.empty()) { |
252 | 250 | return std::make_unique<ECDSA_Verification_Operation>(*this, params); |
253 | 250 | } |
254 | | |
255 | 0 | throw Provider_Not_Found(algo_name(), provider); |
256 | 250 | } |
257 | | |
258 | | std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_x509_verification_op( |
259 | 0 | const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const { |
260 | 0 | if(provider == "base" || provider.empty()) { |
261 | 0 | return std::make_unique<ECDSA_Verification_Operation>(*this, signature_algorithm); |
262 | 0 | } |
263 | | |
264 | 0 | throw Provider_Not_Found(algo_name(), provider); |
265 | 0 | } |
266 | | |
267 | | std::unique_ptr<PK_Ops::Signature> ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng, |
268 | | std::string_view params, |
269 | 93 | std::string_view provider) const { |
270 | 93 | if(provider == "base" || provider.empty()) { |
271 | 93 | return std::make_unique<ECDSA_Signature_Operation>(*this, params, rng); |
272 | 93 | } |
273 | | |
274 | 0 | throw Provider_Not_Found(algo_name(), provider); |
275 | 93 | } |
276 | | |
277 | | } // namespace Botan |