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

randombit / botan / 13210394651

08 Feb 2025 12:33AM UTC coverage: 91.656% (-0.006%) from 91.662%
13210394651

push

github

web-flow
Merge pull request #4642 from randombit/jack/target-info-header

Add internal target_info.h header

94837 of 103471 relevant lines covered (91.66%)

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

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

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

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

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

44
   BigInt x = r;
21✔
45

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

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

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

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

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

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

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

73
}  // namespace
74

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

79
std::unique_ptr<Private_Key> ECDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const {
28✔
80
   return std::make_unique<ECDSA_PrivateKey>(rng, domain());
56✔
81
}
82

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

86
   for(uint8_t v = 0; v != 4; ++v) {
11✔
87
      try {
11✔
88
         const auto R = recover_ecdsa_public_key(this->domain(), msg, r, s, v);
11✔
89

90
         if(R.serialize_compressed() == this_key) {
22✔
91
            return v;
20✔
92
         }
93
      } catch(Decoding_Error&) {
11✔
94
         // try the next v
95
      }
×
96
   }
97

98
   throw Internal_Error("Could not determine ECDSA recovery parameter");
×
99
}
10✔
100

101
std::unique_ptr<Public_Key> ECDSA_PrivateKey::public_key() const {
486✔
102
   return std::make_unique<ECDSA_PublicKey>(domain(), _public_ec_point());
486✔
103
}
104

105
bool ECDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
32✔
106
   if(!EC_PrivateKey::check_key(rng, strong)) {
32✔
107
      return false;
108
   }
109

110
   if(!strong) {
32✔
111
      return true;
112
   }
113

114
   return KeyPair::signature_consistency_check(rng, *this, "SHA-256");
31✔
115
}
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) :
578✔
125
            PK_Ops::Signature_with_Hash(padding),
126
            m_group(ecdsa.domain()),
578✔
127
            m_x(ecdsa._private_key()),
578✔
128
            m_b(EC_Scalar::random(m_group, rng)),
578✔
129
            m_b_inv(m_b.invert()) {
1,156✔
130
#if defined(BOTAN_HAS_RFC6979_GENERATOR)
131
         m_rfc6979 = std::make_unique<RFC6979_Nonce_Generator>(
1,734✔
132
            this->rfc6979_hash_function(), m_group.get_order_bits(), ecdsa._private_key());
1,156✔
133
#endif
134
      }
578✔
135

136
      size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
108✔
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
      std::vector<BigInt> m_ws;
151

152
      EC_Scalar m_b;
153
      EC_Scalar m_b_inv;
154
};
155

156
AlgorithmIdentifier ECDSA_Signature_Operation::algorithm_identifier() const {
62✔
157
   const std::string full_name = "ECDSA/" + hash_function();
124✔
158
   const OID oid = OID::from_string(full_name);
62✔
159
   return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
62✔
160
}
62✔
161

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

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

171
   const auto r = EC_Scalar::gk_x_mod_order(k, rng, m_ws);
3,122✔
172

173
   // Blind the inversion of k
174
   const auto k_inv = (m_b * k).invert() * m_b;
6,244✔
175

176
   /*
177
   * Blind the input message and compute x*r+m as (x*r*b + m*b)/b
178
   */
179
   m_b.square_self();
3,122✔
180
   m_b_inv.square_self();
3,122✔
181

182
   const auto xr_m = ((m_x * m_b) * r) + (m * m_b);
3,122✔
183

184
   const auto s = (k_inv * xr_m) * m_b_inv;
3,122✔
185

186
   // With overwhelming probability, a bug rather than actual zero r/s
187
   if(r.is_zero() || s.is_zero()) {
3,122✔
188
      throw Internal_Error("During ECDSA signature generated zero r/s");
×
189
   }
190

191
   return EC_Scalar::serialize_pair(r, s);
6,244✔
192
}
3,122✔
193

194
/**
195
* ECDSA verification operation
196
*/
197
class ECDSA_Verification_Operation final : public PK_Ops::Verification_with_Hash {
×
198
   public:
199
      ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, std::string_view padding) :
12,670✔
200
            PK_Ops::Verification_with_Hash(padding), m_group(ecdsa.domain()), m_gy_mul(ecdsa._public_ec_point()) {}
12,670✔
201

202
      ECDSA_Verification_Operation(const ECDSA_PublicKey& ecdsa, const AlgorithmIdentifier& alg_id) :
2,830✔
203
            PK_Ops::Verification_with_Hash(alg_id, "ECDSA", true),
204
            m_group(ecdsa.domain()),
2,830✔
205
            m_gy_mul(ecdsa._public_ec_point()) {}
5,660✔
206

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

209
   private:
210
      const EC_Group m_group;
211
      const EC_Group::Mul2Table m_gy_mul;
212
};
213

214
bool ECDSA_Verification_Operation::verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) {
26,829✔
215
   if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
26,829✔
216
      const auto& [r, s] = rs.value();
22,388✔
217

218
      if(r.is_nonzero() && s.is_nonzero()) {
44,776✔
219
         const auto m = EC_Scalar::from_bytes_with_trunc(m_group, msg);
22,388✔
220

221
         const auto w = s.invert_vartime();
22,388✔
222

223
         // Check if r == x_coord(g*w*m + y*w*r) % n
224
         return m_gy_mul.mul2_vartime_x_mod_order_eq(r, w, m, r);
22,388✔
225
      }
22,388✔
226
   }
22,388✔
227

228
   return false;
4,441✔
229
}
230

231
}  // namespace
232

233
std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_verification_op(std::string_view params,
49,372✔
234
                                                                              std::string_view provider) const {
235
   if(provider == "base" || provider.empty()) {
61,616✔
236
      return std::make_unique<ECDSA_Verification_Operation>(*this, params);
12,670✔
237
   }
238

239
   throw Provider_Not_Found(algo_name(), provider);
73,404✔
240
}
241

242
std::unique_ptr<PK_Ops::Verification> ECDSA_PublicKey::create_x509_verification_op(
2,830✔
243
   const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const {
244
   if(provider == "base" || provider.empty()) {
2,830✔
245
      return std::make_unique<ECDSA_Verification_Operation>(*this, signature_algorithm);
2,830✔
246
   }
247

248
   throw Provider_Not_Found(algo_name(), provider);
×
249
}
250

251
std::unique_ptr<PK_Ops::Signature> ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng,
890✔
252
                                                                         std::string_view params,
253
                                                                         std::string_view provider) const {
254
   if(provider == "base" || provider.empty()) {
996✔
255
      return std::make_unique<ECDSA_Signature_Operation>(*this, params, rng);
578✔
256
   }
257

258
   throw Provider_Not_Found(algo_name(), provider);
624✔
259
}
260

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

© 2026 Coveralls, Inc