• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 30496461195

29 Jul 2026 08:43PM UTC coverage: 89.519% (+0.09%) from 89.425%
30496461195

push

github

web-flow
Merge pull request #5776 from Rohde-Schwarz/fix/tls_anvil

116233 of 129841 relevant lines covered (89.52%)

10620449.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.96
/src/lib/pubkey/ecdsa/ecdsa.cpp
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/ec_group.h>
14
#include <botan/internal/keypair.h>
15
#include <botan/internal/pk_ops_impl.h>
16

17
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
18
   #include <botan/internal/rfc6979.h>
19
#endif
20

21
namespace Botan {
22

23
namespace {
24

25
EC_AffinePoint recover_ecdsa_public_key(
21✔
26
   const EC_Group& group, const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s, uint8_t v) {
27
   if(group.has_cofactor()) {
21✔
28
      throw Invalid_Argument("ECDSA public key recovery only supported for prime order groups");
×
29
   }
30

31
   if(v >= 4) {
21✔
32
      throw Invalid_Argument("Unexpected v param for ECDSA public key recovery");
×
33
   }
34

35
   const BigInt& group_order = group.get_order();
21✔
36

37
   if(r <= 0 || r >= group_order || s <= 0 || s >= group_order) {
84✔
38
      throw Invalid_Argument("Out of range r/s cannot recover ECDSA public key");
×
39
   }
40

41
   const uint8_t y_odd = v % 2;
21✔
42
   const bool add_order = (v >> 1) == 0x01;
21✔
43
   const size_t p_bytes = group.get_p_bytes();
21✔
44

45
   BigInt x = r;
21✔
46

47
   if(add_order) {
21✔
48
      x += group_order;
×
49
   }
50

51
   if(x.bytes() <= p_bytes) {
21✔
52
      std::vector<uint8_t> X(p_bytes + 1);
21✔
53

54
      X[0] = 0x02 | y_odd;
21✔
55
      x.serialize_to(std::span{X}.subspan(1));
21✔
56

57
      if(auto R = EC_AffinePoint::deserialize_compressed(group, X)) {
21✔
58
         // Compute r_inv * (-eG + s*R)
59
         const auto ne = EC_Scalar::from_bytes_with_trunc(group, msg).negate();
21✔
60
         const auto ss = EC_Scalar::from_bigint(group, s);
21✔
61

62
         const auto r_inv = EC_Scalar::from_bigint(group, r).invert_vartime();
21✔
63

64
         const EC_Group::Mul2Table GR_mul(R.value());
21✔
65
         if(auto egsr = GR_mul.mul2_vartime(ne * r_inv, ss * r_inv)) {
21✔
66
            return egsr.value();
42✔
67
         }
×
68
      }
42✔
69
   }
×
70

71
   throw Decoding_Error("Failed to recover ECDSA public key from signature/msg pair");
×
72
}
42✔
73

74
}  // namespace
75

76
ECDSA_PublicKey::ECDSA_PublicKey(
10✔
77
   const EC_Group& group, const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s, uint8_t v) :
10✔
78
      EC_PublicKey(group, recover_ecdsa_public_key(group, msg, r, s, v)) {}
10✔
79

80
std::optional<size_t> ECDSA_PublicKey::_signature_element_size_for_DER_encoding() const {
20,503✔
81
   return domain().get_order_bytes();
20,503✔
82
}
83

84
std::unique_ptr<Private_Key> ECDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const {
96✔
85
   return std::make_unique<ECDSA_PrivateKey>(rng, domain());
192✔
86
}
87

88
uint8_t ECDSA_PublicKey::recovery_param(const std::vector<uint8_t>& msg, const BigInt& r, const BigInt& s) const {
10✔
89
   const auto this_key = this->_public_ec_point().serialize_compressed();
10✔
90

91
   for(uint8_t v = 0; v != 4; ++v) {
11✔
92
      try {
11✔
93
         const auto R = recover_ecdsa_public_key(this->domain(), msg, r, s, v);
11✔
94

95
         if(R.serialize_compressed() == this_key) {
22✔
96
            return v;
20✔
97
         }
98
      } catch(Decoding_Error&) {
11✔
99
         // try the next v
100
      }
×
101
   }
102

103
   throw Internal_Error("Could not determine ECDSA recovery parameter");
×
104
}
10✔
105

106
std::unique_ptr<Public_Key> ECDSA_PrivateKey::public_key() const {
549✔
107
   return std::make_unique<ECDSA_PublicKey>(domain(), _public_ec_point());
549✔
108
}
109

110
bool ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
43✔
111
   if(!EC_PrivateKey::check_key(rng, strong)) {
43✔
112
      return false;
113
   }
114

115
   if(!strong) {
43✔
116
      return true;
117
   }
118

119
   return KeyPair::signature_consistency_check(rng, *this, "SHA-256");
42✔
120
}
121

122
namespace {
123

124
/**
125
* ECDSA signature operation
126
*/
127
class ECDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash {
×
128
   public:
129
      ECDSA_Signature_Operation(const ECDSA_PrivateKey& ecdsa, std::string_view padding, RandomNumberGenerator& rng) :
2,253✔
130
            PK_Ops::Signature_with_Hash(padding),
131
            m_group(ecdsa.domain()),
2,253✔
132
            m_x(ecdsa._private_key()),
2,253✔
133
            m_b(EC_Scalar::random(m_group, rng)) {
4,506✔
134
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
135
         m_rfc6979 = std::make_unique<RFC6979_Nonce_Generator>(
6,759✔
136
            this->rfc6979_hash_function(), m_group.get_order_bits(), ecdsa._private_key());
4,506✔
137
#endif
138
      }
2,253✔
139

140
      size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
127✔
141

142
      std::vector<uint8_t> raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) override;
143

144
      AlgorithmIdentifier algorithm_identifier() const override;
145

146
   private:
147
      const EC_Group m_group;
148
      const EC_Scalar m_x;
149

150
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
151
      std::unique_ptr<RFC6979_Nonce_Generator> m_rfc6979;
152
#endif
153

154
      EC_Scalar m_b;
155
};
156

157
AlgorithmIdentifier ECDSA_Signature_Operation::algorithm_identifier() const {
1,649✔
158
   const std::string full_name = "ECDSA/" + hash_function();
3,298✔
159
   const OID oid = OID::from_string(full_name);
1,649✔
160
   return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
1,649✔
161
}
1,649✔
162

163
std::vector<uint8_t> ECDSA_Signature_Operation::raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
6,019✔
164
   const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg);
6,019✔
165

166
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
167
   const auto k = m_rfc6979->nonce_for(m_group, m);
6,019✔
168
#else
169
   const auto k = EC_Scalar::random(m_group, rng);
170
#endif
171

172
   /*
173
   * Blind the inputs
174
   *
175
   * Here we are computing (x*r+m)/k
176
   *
177
   * Instead have a random b and compute (k*b)^-1
178
   *
179
   * Then compute (x*r+m) as (x*r*b + m*b)
180
   *
181
   * Finally (x*r*b + m*b)/(k*b) = (x*r+m)/k
182
   *
183
   * This effectively blinds both the inversion as well as the various scalar
184
   * multiplications. All of these operations should be constant-time anyway but
185
   * blinding is very cheap and may help if either the compiler introduces
186
   * variable-time behavior, or for the case of EM/power side channel attacks [1].
187
   *
188
   * [1] But note that such attacks are currently outside of Botan's threat model.
189
   *
190
   * NOTE: if you change anything here also update ECDSA_Timing_Test
191
   * in cli/timing_tests.cpp to use the same formulas
192
   */
193
   const auto r = EC_Scalar::gk_x_mod_order(k, rng);
6,019✔
194

195
   const auto k_inv = (m_b * k).invert();
6,019✔
196

197
   const auto xr_m = ((m_x * m_b) * r) + (m * m_b);
6,019✔
198

199
   const auto s = (k_inv * xr_m);
6,019✔
200

201
   // Generate the next blinding value via modular squaring
202
   m_b.square_self();
6,019✔
203

204
   // With overwhelming probability, a bug rather than actual zero r/s
205
   if(r.is_zero() || s.is_zero()) {
6,019✔
206
      throw Internal_Error("During ECDSA signature generated zero r/s");
×
207
   }
208

209
   return EC_Scalar::serialize_pair(r, s);
12,038✔
210
}
6,019✔
211

212
/**
213
* ECDSA verification operation
214
*/
215
class ECDSA_Verification_Operation final : public PK_Ops::Verification_with_Hash {
×
216
   public:
217
      ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, std::string_view padding) :
12,855✔
218
            PK_Ops::Verification_with_Hash(padding), m_group(ecdsa.domain()), m_gy_mul(ecdsa._public_ec_point()) {}
12,855✔
219

220
      ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, const AlgorithmIdentifier& alg_id) :
3,838✔
221
            /*
222
            * RFC 5758 Section 3.2 is clear that for ECDSA signatures the parameters field is empty
223
            *
224
            *    When the [ecdsa-with-SHA*] algorithm identifier appears in the algorithm
225
            *    field as an AlgorithmIdentifier, the encoding MUST omit the parameters
226
            *    field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
227
            *    component, the OID [ecdsa-with-SHA*].
228
            *
229
            * However ECDSA is old enough and widely implemented enough that many non-conformant
230
            * implementations which emit X509 signatures using an explicit NULL parameter do exist,
231
            * so accept it here.
232
            */
233
            PK_Ops::Verification_with_Hash(alg_id, "ECDSA", true),
234
            m_group(ecdsa.domain()),
3,838✔
235
            m_gy_mul(ecdsa._public_ec_point()) {}
7,676✔
236

237
      bool verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) override;
238

239
   private:
240
      const EC_Group m_group;
241
      const EC_Group::Mul2Table m_gy_mul;
242
};
243

244
bool ECDSA_Verification_Operation::verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) {
39,150✔
245
   if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
39,150✔
246
      const auto& [r, s] = rs.value();
33,026✔
247

248
      if(r.is_nonzero() && s.is_nonzero()) {
66,052✔
249
         const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg);
33,026✔
250

251
         const auto w = s.invert_vartime();
33,026✔
252

253
         // Check if r == x_coord(g*w*m + y*w*r) % n
254
         return m_gy_mul.mul2_vartime_x_mod_order_eq(r, w, m, r);
33,026✔
255
      }
33,026✔
256
   }
33,026✔
257

258
   return false;
6,124✔
259
}
260

261
}  // namespace
262

263
std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_verification_op(std::string_view params,
49,605✔
264
                                                                              std::string_view provider) const {
265
   if(provider == "base" || provider.empty()) {
61,865✔
266
      return std::make_unique<ECDSA_Verification_Operation>(*this, params);
12,855✔
267
   }
268

269
   throw Provider_Not_Found(algo_name(), provider);
73,500✔
270
}
271

272
std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_x509_verification_op(
3,838✔
273
   const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const {
274
   if(provider == "base" || provider.empty()) {
3,838✔
275
      return std::make_unique<ECDSA_Verification_Operation>(*this, signature_algorithm);
3,838✔
276
   }
277

278
   throw Provider_Not_Found(algo_name(), provider);
×
279
}
280

281
std::unique_ptr<PK_Ops::Signature> ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
2,565✔
282
                                                                         std::string_view params,
283
                                                                         std::string_view provider) const {
284
   if(provider == "base" || provider.empty()) {
2,671✔
285
      return std::make_unique<ECDSA_Signature_Operation>(*this, params, rng);
2,253✔
286
   }
287

288
   throw Provider_Not_Found(algo_name(), provider);
624✔
289
}
290

291
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc