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

randombit / botan / 5079590438

25 May 2023 12:28PM UTC coverage: 92.228% (+0.5%) from 91.723%
5079590438

Pull #3502

github

Pull Request #3502: Apply clang-format to the codebase

75589 of 81959 relevant lines covered (92.23%)

12139530.51 hits per line

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

91.89
/src/lib/pubkey/eckcdsa/eckcdsa.cpp
1
/*
2
* ECKCDSA (ISO/IEC 14888-3:2006/Cor.2:2009)
3
* (C) 2016 René Korthaus, Sirrix AG
4
* (C) 2018 Jack Lloyd
5
* (C) 2023 Philippe Lieser - Rohde & Schwarz Cybersecurity
6
*
7
* Botan is released under the Simplified BSD License (see license.txt)
8
*/
9

10
#include <botan/eckcdsa.h>
11

12
#include <botan/hash.h>
13
#include <botan/reducer.h>
14
#include <botan/rng.h>
15
#include <botan/internal/fmt.h>
16
#include <botan/internal/keypair.h>
17
#include <botan/internal/parsing.h>
18
#include <botan/internal/pk_ops_impl.h>
19
#include <botan/internal/point_mul.h>
20
#include <botan/internal/scan_name.h>
21

22
namespace Botan {
23

24
std::unique_ptr<Public_Key> ECKCDSA_PrivateKey::public_key() const {
3✔
25
   return std::make_unique<ECKCDSA_PublicKey>(domain(), public_point());
3✔
26
}
27

28
bool ECKCDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
3✔
29
   if(!public_point().on_the_curve()) {
3✔
30
      return false;
31
   }
32

33
   if(!strong) {
3✔
34
      return true;
35
   }
36

37
   return KeyPair::signature_consistency_check(rng, *this, "SHA-256");
3✔
38
}
39

40
namespace {
41

42
std::unique_ptr<HashFunction> eckcdsa_signature_hash(std::string_view padding) {
77✔
43
   if(auto hash = HashFunction::create(padding))
77✔
44
      return hash;
77✔
45

46
   SCAN_Name req(padding);
×
47

48
   if(req.algo_name() == "EMSA1" && req.arg_count() == 1) {
×
49
      if(auto hash = HashFunction::create(req.arg(0)))
×
50
         return hash;
×
51
   }
52

53
   // intentionally not supporting Raw for ECKCDSA, we need to know
54
   // the length in advance which complicates the logic for Raw
55

56
   throw Algorithm_Not_Found(padding);
×
57
}
×
58

59
std::unique_ptr<HashFunction> eckcdsa_signature_hash(const AlgorithmIdentifier& alg_id) {
39✔
60
   const auto oid_info = split_on(alg_id.oid().to_formatted_string(), '/');
39✔
61

62
   if(oid_info.size() != 2 || oid_info[0] != "ECKCDSA") {
39✔
63
      throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier OID {} in association with ECKCDSA key", alg_id.oid()));
×
64
   }
65

66
   if(!alg_id.parameters_are_empty()) {
39✔
67
      throw Decoding_Error("Unexpected non-empty AlgorithmIdentifier parameters for ECKCDSA");
×
68
   }
69

70
   return HashFunction::create_or_throw(oid_info[1]);
78✔
71
}
39✔
72

73
std::vector<uint8_t> eckcdsa_prefix(const PointGFp& point, size_t order_bytes, size_t hash_block_size) {
116✔
74
   const BigInt public_x = point.get_affine_x();
116✔
75
   const BigInt public_y = point.get_affine_y();
116✔
76

77
   std::vector<uint8_t> prefix(2 * order_bytes);
116✔
78
   BigInt::encode_1363(&prefix[0], order_bytes, public_x);
116✔
79
   BigInt::encode_1363(&prefix[order_bytes], order_bytes, public_y);
116✔
80

81
   // Either truncate or zero-extend to match the hash block size
82
   prefix.resize(hash_block_size);
116✔
83

84
   return prefix;
116✔
85
}
232✔
86

87
/**
88
 * @brief Truncate hash output if needed.
89
 *
90
 * If the output length of the hash function exceeds the size of the group order,
91
 * ISO/IEC 14888-3:2018 specifies a truncation of the hash output
92
 * when calculating the witness R (the first part of the signature) and H.
93
 *
94
 * The truncation is specified as follows:
95
 *
96
 * R = I2BS(beta', BS2I(gamma, R) mod 2^beta')
97
 * H = I2BS(beta', BS2I(gamma, H) mod 2^beta')
98
 *
99
 * where
100
 * - gamma: the output bit-length of the hash-function
101
 * - beta: the bit-length of the prime number q (i.e. the group order size)
102
 * - beta' = 8 * ceil(beta / 8)
103
 *
104
 * This essentially means a truncation on the byte level
105
 * happens from the low side of the hash.
106
 *
107
 * @param[in,out] digest The hash output to potentially truncate.
108
 * @param[in] group_order_bytes Size of the group order.
109
 */
110
void truncate_hash_if_needed(secure_vector<uint8_t>& digest, size_t group_order_bytes) {
743✔
111
   if(digest.size() > group_order_bytes) {
743✔
112
      const size_t bytes_to_truncate = digest.size() - group_order_bytes;
94✔
113
      digest.erase(digest.begin(), digest.begin() + bytes_to_truncate);
94✔
114
   }
115
}
743✔
116

117
/**
118
* ECKCDSA signature operation
119
*/
120
class ECKCDSA_Signature_Operation final : public PK_Ops::Signature {
×
121
   public:
122
      ECKCDSA_Signature_Operation(const ECKCDSA_PrivateKey& eckcdsa, std::string_view padding) :
57✔
123
            m_group(eckcdsa.domain()),
57✔
124
            m_x(eckcdsa.private_value()),
57✔
125
            m_hash(eckcdsa_signature_hash(padding)),
57✔
126
            m_prefix_used(false) {
114✔
127
         m_prefix = eckcdsa_prefix(eckcdsa.public_point(), m_group.get_order_bytes(), m_hash->hash_block_size());
57✔
128
      }
57✔
129

130
      void update(const uint8_t msg[], size_t msg_len) override {
67✔
131
         if(!m_prefix_used) {
67✔
132
            m_hash->update(m_prefix.data(), m_prefix.size());
67✔
133
            m_prefix_used = true;
67✔
134
         }
135
         m_hash->update(msg, msg_len);
67✔
136
      }
67✔
137

138
      secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override {
67✔
139
         m_prefix_used = false;
67✔
140
         secure_vector<uint8_t> digest = m_hash->final();
67✔
141
         truncate_hash_if_needed(digest, m_group.get_order_bytes());
67✔
142
         return raw_sign(digest.data(), digest.size(), rng);
67✔
143
      }
67✔
144

145
      size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
11✔
146

147
      AlgorithmIdentifier algorithm_identifier() const override;
148

149
      std::string hash_function() const override { return m_hash->name(); }
59✔
150

151
   private:
152
      secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng);
153

154
      const EC_Group m_group;
155
      const BigInt& m_x;
156
      std::unique_ptr<HashFunction> m_hash;
157
      std::vector<uint8_t> m_prefix;
158
      std::vector<BigInt> m_ws;
159
      bool m_prefix_used;
160
};
161

162
AlgorithmIdentifier ECKCDSA_Signature_Operation::algorithm_identifier() const {
37✔
163
   const std::string full_name = "ECKCDSA/" + m_hash->name();
37✔
164
   const OID oid = OID::from_string(full_name);
37✔
165
   return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
37✔
166
}
37✔
167

168
secure_vector<uint8_t> ECKCDSA_Signature_Operation::raw_sign(const uint8_t msg[],
67✔
169
                                                             size_t msg_len,
170
                                                             RandomNumberGenerator& rng) {
171
   const BigInt k = m_group.random_scalar(rng);
67✔
172
   const BigInt k_times_P_x = m_group.blinded_base_point_multiply_x(k, rng, m_ws);
67✔
173

174
   secure_vector<uint8_t> to_be_hashed(k_times_P_x.bytes());
67✔
175
   k_times_P_x.binary_encode(to_be_hashed.data());
67✔
176

177
   auto hash = m_hash->new_object();
67✔
178
   hash->update(to_be_hashed);
67✔
179
   secure_vector<uint8_t> c = hash->final();
67✔
180
   truncate_hash_if_needed(c, m_group.get_order_bytes());
67✔
181

182
   const BigInt r(c.data(), c.size());
67✔
183

184
   BOTAN_ASSERT_NOMSG(msg_len == c.size());
67✔
185
   xor_buf(c, msg, c.size());
67✔
186
   BigInt w(c.data(), c.size());
67✔
187
   w = m_group.mod_order(w);
67✔
188

189
   const BigInt s = m_group.multiply_mod_order(m_x, k - w);
67✔
190
   if(s.is_zero())
134✔
191
      throw Internal_Error("During ECKCDSA signature generation created zero s");
×
192

193
   secure_vector<uint8_t> output = BigInt::encode_1363(r, c.size());
67✔
194
   output += BigInt::encode_1363(s, m_group.get_order_bytes());
67✔
195
   return output;
67✔
196
}
536✔
197

198
/**
199
* ECKCDSA verification operation
200
*/
201
class ECKCDSA_Verification_Operation final : public PK_Ops::Verification {
×
202
   public:
203
      ECKCDSA_Verification_Operation(const ECKCDSA_PublicKey& eckcdsa, std::string_view padding) :
20✔
204
            m_group(eckcdsa.domain()),
20✔
205
            m_gy_mul(m_group.get_base_point(), eckcdsa.public_point()),
20✔
206
            m_hash(eckcdsa_signature_hash(padding)),
20✔
207
            m_prefix_used(false) {
40✔
208
         m_prefix = eckcdsa_prefix(eckcdsa.public_point(), m_group.get_order_bytes(), m_hash->hash_block_size());
20✔
209
      }
20✔
210

211
      ECKCDSA_Verification_Operation(const ECKCDSA_PublicKey& eckcdsa, const AlgorithmIdentifier& alg_id) :
39✔
212
            m_group(eckcdsa.domain()),
39✔
213
            m_gy_mul(m_group.get_base_point(), eckcdsa.public_point()),
39✔
214
            m_hash(eckcdsa_signature_hash(alg_id)),
39✔
215
            m_prefix_used(false) {
78✔
216
         m_prefix = eckcdsa_prefix(eckcdsa.public_point(), m_group.get_order_bytes(), m_hash->hash_block_size());
39✔
217
      }
39✔
218

219
      void update(const uint8_t msg[], size_t msg_len) override;
220

221
      bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
222

223
      std::string hash_function() const override { return m_hash->name(); }
39✔
224

225
   private:
226
      bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len);
227

228
      const EC_Group m_group;
229
      const EC_Point_Multi_Point_Precompute m_gy_mul;
230
      std::vector<uint8_t> m_prefix;
231
      std::unique_ptr<HashFunction> m_hash;
232
      bool m_prefix_used;
233
};
234

235
void ECKCDSA_Verification_Operation::update(const uint8_t msg[], size_t msg_len) {
310✔
236
   if(!m_prefix_used) {
310✔
237
      m_prefix_used = true;
310✔
238
      m_hash->update(m_prefix.data(), m_prefix.size());
310✔
239
   }
240
   m_hash->update(msg, msg_len);
310✔
241
}
310✔
242

243
bool ECKCDSA_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len) {
310✔
244
   m_prefix_used = false;
310✔
245
   secure_vector<uint8_t> digest = m_hash->final();
310✔
246
   truncate_hash_if_needed(digest, m_group.get_order_bytes());
310✔
247
   return verify(digest.data(), digest.size(), sig, sig_len);
310✔
248
}
310✔
249

250
bool ECKCDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) {
310✔
251
   //calculate size of r
252

253
   const size_t order_bytes = m_group.get_order_bytes();
310✔
254

255
   const size_t size_r = std::min(msg_len, order_bytes);
310✔
256
   if(sig_len != size_r + order_bytes) {
310✔
257
      return false;
258
   }
259

260
   secure_vector<uint8_t> r(sig, sig + size_r);
310✔
261

262
   // check that 0 < s < q
263
   const BigInt s(sig + size_r, order_bytes);
310✔
264

265
   if(s <= 0 || s >= m_group.get_order()) {
310✔
266
      return false;
11✔
267
   }
268

269
   secure_vector<uint8_t> r_xor_e(r);
299✔
270
   xor_buf(r_xor_e, msg, r.size());
299✔
271
   BigInt w(r_xor_e.data(), r_xor_e.size());
299✔
272
   w = m_group.mod_order(w);
299✔
273

274
   const EC_Point q = m_gy_mul.multi_exp(w, s);
299✔
275
   if(q.is_zero()) {
598✔
276
      return false;
277
   }
278

279
   const BigInt q_x = q.get_affine_x();
299✔
280
   secure_vector<uint8_t> c(q_x.bytes());
299✔
281
   q_x.binary_encode(c.data());
299✔
282
   auto c_hash = m_hash->new_object();
299✔
283
   c_hash->update(c.data(), c.size());
299✔
284
   secure_vector<uint8_t> v = c_hash->final();
299✔
285
   truncate_hash_if_needed(v, m_group.get_order_bytes());
299✔
286

287
   return (v == r);
299✔
288
}
2,425✔
289

290
}
291

292
std::unique_ptr<PK_Ops::Verification> ECKCDSA_PublicKey::create_verification_op(std::string_view params,
53✔
293
                                                                                std::string_view provider) const {
294
   if(provider == "base" || provider.empty())
64✔
295
      return std::make_unique<ECKCDSA_Verification_Operation>(*this, params);
20✔
296
   throw Provider_Not_Found(algo_name(), provider);
66✔
297
}
298

299
std::unique_ptr<PK_Ops::Verification> ECKCDSA_PublicKey::create_x509_verification_op(
39✔
300
   const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const {
301
   if(provider == "base" || provider.empty())
39✔
302
      return std::make_unique<ECKCDSA_Verification_Operation>(*this, signature_algorithm);
39✔
303

304
   throw Provider_Not_Found(algo_name(), provider);
×
305
}
306

307
std::unique_ptr<PK_Ops::Signature> ECKCDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
90✔
308
                                                                           std::string_view params,
309
                                                                           std::string_view provider) const {
310
   if(provider == "base" || provider.empty())
101✔
311
      return std::make_unique<ECKCDSA_Signature_Operation>(*this, params);
57✔
312
   throw Provider_Not_Found(algo_name(), provider);
66✔
313
}
314

315
}
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

© 2025 Coveralls, Inc