Coverage Report

Created: 2025-10-13 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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