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

randombit / botan / 12847647540

18 Jan 2025 09:39PM UTC coverage: 91.211% (+0.003%) from 91.208%
12847647540

Pull #4555

github

web-flow
Merge 449f252b4 into 5e2bd0320
Pull Request #4555: Remove the workspace argument to various ECC interfaces

93418 of 102420 relevant lines covered (91.21%)

11426977.61 hits per line

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

95.35
/src/lib/pubkey/sm2/sm2.cpp
1
/*
2
* SM2 Signatures
3
* (C) 2017,2018 Ribose Inc
4
* (C) 2018,2024 Jack Lloyd
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8

9
#include <botan/sm2.h>
10

11
#include <botan/hash.h>
12
#include <botan/numthry.h>
13
#include <botan/internal/keypair.h>
14
#include <botan/internal/loadstor.h>
15
#include <botan/internal/parsing.h>
16
#include <botan/internal/pk_ops_impl.h>
17

18
namespace Botan {
19

20
std::string SM2_PublicKey::algo_name() const {
207✔
21
   return "SM2";
207✔
22
}
23

24
std::unique_ptr<Public_Key> SM2_PrivateKey::public_key() const {
7✔
25
   return std::make_unique<SM2_Signature_PublicKey>(domain(), _public_ec_point());
7✔
26
}
27

28
bool SM2_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
4✔
29
   if(!EC_PrivateKey::check_key(rng, strong)) {
4✔
30
      return false;
31
   }
32

33
   // SM2 has an oddity in private key generation when compared to
34
   // other EC*DSA style signature algorithms described in ISO14888-3:
35
   // the private key x MUST be in [0, q-1) instead of [0, q).
36
   //
37
   // The lower bound is already checked by the default impl
38
   if(private_value() >= domain().get_order() - 1) {
8✔
39
      return false;
40
   }
41

42
   if(!strong) {
4✔
43
      return true;
44
   }
45

46
   return KeyPair::signature_consistency_check(rng, *this, "user@example.com,SM3");
2✔
47
}
48

49
SM2_PrivateKey::SM2_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
20✔
50
      EC_PrivateKey(alg_id, key_bits),
51
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
20✔
52
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
40✔
53

54
SM2_PrivateKey::SM2_PrivateKey(EC_Group group, EC_Scalar x) :
×
55
      EC_PrivateKey(std::move(group), std::move(x)),
56
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
×
57
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
×
58

59
SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group) :
10✔
60
      EC_PrivateKey(rng, std::move(group)),
61
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
10✔
62
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
20✔
63

64
SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group, const BigInt& x) :
15✔
65
      EC_PrivateKey(rng, std::move(group), x),
66
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
15✔
67
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
30✔
68

69
std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
28✔
70
                                    std::string_view user_id,
71
                                    const EC_Group& group,
72
                                    const EC_AffinePoint& pubkey) {
73
   if(user_id.size() >= 8192) {
28✔
74
      throw Invalid_Argument("SM2 user id too long to represent");
×
75
   }
76

77
   const uint16_t uid_len = static_cast<uint16_t>(8 * user_id.size());
28✔
78

79
   hash.update(get_byte<0>(uid_len));
28✔
80
   hash.update(get_byte<1>(uid_len));
28✔
81
   hash.update(user_id);
28✔
82

83
   const size_t p_bytes = group.get_p_bytes();
28✔
84

85
   hash.update(group.get_a().serialize(p_bytes));
28✔
86
   hash.update(group.get_b().serialize(p_bytes));
28✔
87
   hash.update(group.get_g_x().serialize(p_bytes));
28✔
88
   hash.update(group.get_g_y().serialize(p_bytes));
28✔
89
   hash.update(pubkey.xy_bytes());
28✔
90

91
   return hash.final<std::vector<uint8_t>>();
28✔
92
}
93

94
namespace {
95

96
/**
97
* SM2 signature operation
98
*/
99
class SM2_Signature_Operation final : public PK_Ops::Signature {
×
100
   public:
101
      SM2_Signature_Operation(const SM2_PrivateKey& sm2, std::string_view ident, std::string_view hash) :
15✔
102
            m_group(sm2.domain()), m_x(sm2._private_key()), m_da_inv(sm2._get_da_inv()) {
15✔
103
         if(hash == "Raw") {
17✔
104
            // m_hash is null, m_za is empty
105
         } else {
106
            m_hash = HashFunction::create_or_throw(hash);
13✔
107
            // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
108
            m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
13✔
109
            m_hash->update(m_za);
13✔
110
         }
111
      }
15✔
112

113
      size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
10✔
114

115
      void update(std::span<const uint8_t> input) override {
19✔
116
         if(m_hash) {
19✔
117
            m_hash->update(input);
17✔
118
         } else {
119
            m_digest.insert(m_digest.end(), input.begin(), input.end());
2✔
120
         }
121
      }
19✔
122

123
      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override;
124

125
      std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
4✔
126

127
   private:
128
      const EC_Group m_group;
129
      const EC_Scalar m_x;
130
      const EC_Scalar m_da_inv;
131

132
      std::vector<uint8_t> m_za;
133
      secure_vector<uint8_t> m_digest;
134
      std::unique_ptr<HashFunction> m_hash;
135
};
136

137
std::vector<uint8_t> SM2_Signature_Operation::sign(RandomNumberGenerator& rng) {
20✔
138
   const auto e = [&]() {
60✔
139
      if(m_hash) {
20✔
140
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
18✔
141
         // prepend ZA for next signature if any
142
         m_hash->update(m_za);
18✔
143
         return ie;
18✔
144
      } else {
18✔
145
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
2✔
146
         m_digest.clear();
2✔
147
         return ie;
2✔
148
      }
2✔
149
   }();
20✔
150

151
   const auto k = EC_Scalar::random(m_group, rng);
20✔
152

153
   const auto r = EC_Scalar::gk_x_mod_order(k, rng) + e;
40✔
154
   const auto s = (k - r * m_x) * m_da_inv;
20✔
155

156
   return EC_Scalar::serialize_pair(r, s);
40✔
157
}
20✔
158

159
/**
160
* SM2 verification operation
161
*/
162
class SM2_Verification_Operation final : public PK_Ops::Verification {
×
163
   public:
164
      SM2_Verification_Operation(const SM2_PublicKey& sm2, std::string_view ident, std::string_view hash) :
15✔
165
            m_group(sm2.domain()), m_gy_mul(sm2._public_ec_point()) {
15✔
166
         if(hash == "Raw") {
17✔
167
            // m_hash is null, m_za is empty
168
         } else {
169
            m_hash = HashFunction::create_or_throw(hash);
13✔
170
            // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
171
            m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
13✔
172
            m_hash->update(m_za);
13✔
173
         }
174
      }
15✔
175

176
      void update(std::span<const uint8_t> input) override {
178✔
177
         if(m_hash) {
178✔
178
            m_hash->update(input);
154✔
179
         } else {
180
            m_digest.insert(m_digest.end(), input.begin(), input.end());
24✔
181
         }
182
      }
178✔
183

184
      bool is_valid_signature(std::span<const uint8_t> sig) override;
185

186
      std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
4✔
187

188
   private:
189
      const EC_Group m_group;
190
      const EC_Group::Mul2Table m_gy_mul;
191
      secure_vector<uint8_t> m_digest;
192
      std::vector<uint8_t> m_za;
193
      std::unique_ptr<HashFunction> m_hash;
194
};
195

196
bool SM2_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
179✔
197
   const auto e = [&]() {
537✔
198
      if(m_hash) {
179✔
199
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
155✔
200
         // prepend ZA for next signature if any
201
         m_hash->update(m_za);
155✔
202
         return ie;
155✔
203
      } else {
155✔
204
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
24✔
205
         m_digest.clear();
24✔
206
         return ie;
24✔
207
      }
24✔
208
   }();
179✔
209

210
   if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
179✔
211
      const auto& [r, s] = rs.value();
172✔
212

213
      if(r.is_nonzero() && s.is_nonzero()) {
344✔
214
         const auto t = r + s;
172✔
215
         if(t.is_nonzero()) {
172✔
216
            // Check if r - e = x_coord(g*s + y*t) % n
217
            return m_gy_mul.mul2_vartime_x_mod_order_eq(r - e, s, t);
172✔
218
         }
219
      }
172✔
220
   }
172✔
221
   return false;
7✔
222
}
179✔
223

224
void parse_sm2_param_string(std::string_view params, std::string& userid, std::string& hash) {
30✔
225
   // GM/T 0009-2012 specifies this as the default userid
226
   const std::string default_userid = "1234567812345678";
30✔
227

228
   // defaults:
229
   userid = default_userid;
30✔
230
   hash = "SM3";
30✔
231

232
   /*
233
   * SM2 parameters have the following possible formats:
234
   * Ident [since 2.2.0]
235
   * Ident,Hash [since 2.3.0]
236
   */
237

238
   auto comma = params.find(',');
30✔
239
   if(comma == std::string::npos) {
30✔
240
      userid = params;
38✔
241
   } else {
242
      userid = params.substr(0, comma);
22✔
243
      hash = params.substr(comma + 1, std::string::npos);
44✔
244
   }
245
}
30✔
246

247
}  // namespace
248

249
std::unique_ptr<Private_Key> SM2_PublicKey::generate_another(RandomNumberGenerator& rng) const {
2✔
250
   return std::make_unique<SM2_PrivateKey>(rng, domain());
4✔
251
}
252

253
std::unique_ptr<PK_Ops::Verification> SM2_PublicKey::create_verification_op(std::string_view params,
36✔
254
                                                                            std::string_view provider) const {
255
   if(provider == "base" || provider.empty()) {
45✔
256
      std::string userid, hash;
15✔
257
      parse_sm2_param_string(params, userid, hash);
15✔
258
      return std::make_unique<SM2_Verification_Operation>(*this, userid, hash);
15✔
259
   }
15✔
260

261
   throw Provider_Not_Found(algo_name(), provider);
42✔
262
}
263

264
std::unique_ptr<PK_Ops::Signature> SM2_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
36✔
265
                                                                       std::string_view params,
266
                                                                       std::string_view provider) const {
267
   if(provider == "base" || provider.empty()) {
45✔
268
      std::string userid, hash;
15✔
269
      parse_sm2_param_string(params, userid, hash);
15✔
270
      return std::make_unique<SM2_Signature_Operation>(*this, userid, hash);
15✔
271
   }
15✔
272

273
   throw Provider_Not_Found(algo_name(), provider);
42✔
274
}
275

276
}  // 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