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

randombit / botan / 24101703016

06 Apr 2026 10:36PM UTC coverage: 89.45% (-0.005%) from 89.455%
24101703016

push

github

web-flow
Merge pull request #5521 from randombit/jack/fix-rollup

Rollup of small fixes

105880 of 118368 relevant lines covered (89.45%)

11539723.42 hits per line

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

97.04
/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/ec_group.h>
12
#include <botan/hash.h>
13
#include <botan/internal/keypair.h>
14
#include <botan/internal/loadstor.h>
15
#include <botan/internal/pk_ops_impl.h>
16

17
namespace Botan {
18

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

23
std::optional<size_t> SM2_PublicKey::_signature_element_size_for_DER_encoding() const {
129✔
24
   return domain().get_order_bytes();
129✔
25
}
26

27
std::unique_ptr<Public_Key> SM2_PrivateKey::public_key() const {
35✔
28
   return std::make_unique<SM2_Signature_PublicKey>(domain(), _public_ec_point());
35✔
29
}
30

31
bool SM2_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
14✔
32
   if(!EC_PrivateKey::check_key(rng, strong)) {
14✔
33
      return false;
34
   }
35

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

45
   if(!strong) {
14✔
46
      return true;
47
   }
48

49
   return KeyPair::signature_consistency_check(rng, *this, "user@example.com,SM3");
12✔
50
}
51

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

57
SM2_PrivateKey::SM2_PrivateKey(const EC_Group& group, const EC_Scalar& x) :
19✔
58
      EC_PrivateKey(group, x),
59
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
19✔
60
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
38✔
61

62
SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, const EC_Group& group) :
22✔
63
      EC_PrivateKey(rng, group),
64
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
22✔
65
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
44✔
66

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

72
#if defined(BOTAN_HAS_LEGACY_EC_POINT)
73
std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
74
                                    std::string_view user_id,
75
                                    const EC_Group& group,
76
                                    const EC_Point& pubkey) {
77
   auto apoint = EC_AffinePoint(group, pubkey);
78
   return sm2_compute_za(hash, user_id, group, apoint);
79
}
80
#endif
81

82
std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
106✔
83
                                    std::string_view user_id,
84
                                    const EC_Group& group,
85
                                    const EC_AffinePoint& pubkey) {
86
   if(user_id.size() >= 8192) {
106✔
87
      throw Invalid_Argument("SM2 user id too long to represent");
×
88
   }
89

90
   const uint16_t uid_len = static_cast<uint16_t>(8 * user_id.size());
106✔
91

92
   hash.update(get_byte<0>(uid_len));
106✔
93
   hash.update(get_byte<1>(uid_len));
106✔
94
   hash.update(user_id);
106✔
95

96
   const size_t p_bytes = group.get_p_bytes();
106✔
97

98
   hash.update(group.get_a().serialize(p_bytes));
106✔
99
   hash.update(group.get_b().serialize(p_bytes));
106✔
100
   hash.update(group.get_g_x().serialize(p_bytes));
106✔
101
   hash.update(group.get_g_y().serialize(p_bytes));
106✔
102
   hash.update(pubkey.xy_bytes());
106✔
103

104
   return hash.final<std::vector<uint8_t>>();
106✔
105
}
106

107
namespace {
108

109
/**
110
* SM2 signature operation
111
*/
112
class SM2_Signature_Operation final : public PK_Ops::Signature {
×
113
   public:
114
      SM2_Signature_Operation(const SM2_PrivateKey& sm2, std::string_view ident, std::string_view hash) :
54✔
115
            m_group(sm2.domain()), m_x(sm2._private_key()), m_da_inv(sm2._get_da_inv()) {
54✔
116
         if(hash == "Raw") {
56✔
117
            // m_hash is null, m_za is empty
118
         } else {
119
            m_hash = HashFunction::create_or_throw(hash);
52✔
120
            // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
121
            m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
52✔
122
            m_hash->update(m_za);
52✔
123
         }
124
      }
54✔
125

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

128
      void update(std::span<const uint8_t> input) override {
65✔
129
         if(m_hash) {
65✔
130
            m_hash->update(input);
63✔
131
         } else {
132
            m_digest.insert(m_digest.end(), input.begin(), input.end());
2✔
133
         }
134
      }
65✔
135

136
      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override;
137

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

140
   private:
141
      const EC_Group m_group;
142
      const EC_Scalar m_x;
143
      const EC_Scalar m_da_inv;
144

145
      std::vector<uint8_t> m_za;
146
      secure_vector<uint8_t> m_digest;
147
      std::unique_ptr<HashFunction> m_hash;
148
};
149

150
std::vector<uint8_t> SM2_Signature_Operation::sign(RandomNumberGenerator& rng) {
60✔
151
   const auto e = [&]() {
180✔
152
      if(m_hash) {
60✔
153
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
58✔
154
         // prepend ZA for next signature if any
155
         m_hash->update(m_za);
58✔
156
         return ie;
58✔
157
      } else {
58✔
158
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
2✔
159
         m_digest.clear();
2✔
160
         return ie;
2✔
161
      }
2✔
162
   }();
60✔
163

164
   const auto k = EC_Scalar::random(m_group, rng);
60✔
165

166
   const auto r = EC_Scalar::gk_x_mod_order(k, rng) + e;
120✔
167
   const auto s = (k - r * m_x) * m_da_inv;
60✔
168

169
   // With overwhelming probability, a bug rather than actual zero r/s
170
   if(r.is_zero() || s.is_zero()) {
60✔
171
      throw Internal_Error("During SM2 signature generated zero r/s");
×
172
   }
173

174
   return EC_Scalar::serialize_pair(r, s);
120✔
175
}
60✔
176

177
/**
178
* SM2 verification operation
179
*/
180
class SM2_Verification_Operation final : public PK_Ops::Verification {
×
181
   public:
182
      SM2_Verification_Operation(const SM2_PublicKey& sm2, std::string_view ident, std::string_view hash) :
54✔
183
            m_group(sm2.domain()), m_gy_mul(sm2._public_ec_point()) {
54✔
184
         if(hash == "Raw") {
56✔
185
            // m_hash is null, m_za is empty
186
         } else {
187
            m_hash = HashFunction::create_or_throw(hash);
52✔
188
            // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
189
            m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
52✔
190
            m_hash->update(m_za);
52✔
191
         }
192
      }
54✔
193

194
      void update(std::span<const uint8_t> input) override {
432✔
195
         if(m_hash) {
432✔
196
            m_hash->update(input);
380✔
197
         } else {
198
            m_digest.insert(m_digest.end(), input.begin(), input.end());
52✔
199
         }
200
      }
432✔
201

202
      bool is_valid_signature(std::span<const uint8_t> sig) override;
203

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

206
   private:
207
      const EC_Group m_group;
208
      const EC_Group::Mul2Table m_gy_mul;
209
      secure_vector<uint8_t> m_digest;
210
      std::vector<uint8_t> m_za;
211
      std::unique_ptr<HashFunction> m_hash;
212
};
213

214
bool SM2_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
447✔
215
   const auto e = [&]() {
1,341✔
216
      if(m_hash) {
447✔
217
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
395✔
218
         // prepend ZA for next signature if any
219
         m_hash->update(m_za);
395✔
220
         return ie;
395✔
221
      } else {
395✔
222
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
52✔
223
         m_digest.clear();
52✔
224
         return ie;
52✔
225
      }
52✔
226
   }();
447✔
227

228
   if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
447✔
229
      const auto& [r, s] = rs.value();
397✔
230

231
      if(r.is_nonzero() && s.is_nonzero()) {
794✔
232
         const auto t = r + s;
397✔
233
         if(t.is_nonzero()) {
397✔
234
            // Check if r - e = x_coord(g*s + y*t) % n
235
            return m_gy_mul.mul2_vartime_x_mod_order_eq(r - e, s, t);
397✔
236
         }
237
      }
397✔
238
   }
397✔
239
   return false;
50✔
240
}
447✔
241

242
void parse_sm2_param_string(std::string_view params, std::string& userid, std::string& hash) {
108✔
243
   // GM/T 0009-2012 specifies this as the default userid
244
   const std::string default_userid = "1234567812345678";
108✔
245

246
   // defaults:
247
   userid = default_userid;
108✔
248
   hash = "SM3";
108✔
249

250
   /*
251
   * SM2 parameters have the following possible formats:
252
   * Ident [since 2.2.0]
253
   * Ident,Hash [since 2.3.0]
254
   */
255

256
   auto comma = params.find(',');
108✔
257
   if(comma == std::string::npos) {
108✔
258
      userid = params;
174✔
259
   } else {
260
      userid = params.substr(0, comma);
42✔
261
      hash = params.substr(comma + 1, std::string::npos);
84✔
262
   }
263
}
108✔
264

265
}  // namespace
266

267
std::unique_ptr<Private_Key> SM2_PublicKey::generate_another(RandomNumberGenerator& rng) const {
2✔
268
   return std::make_unique<SM2_PrivateKey>(rng, domain());
4✔
269
}
270

271
std::unique_ptr<PK_Ops::Verification> SM2_PublicKey::create_verification_op(std::string_view params,
75✔
272
                                                                            std::string_view provider) const {
273
   if(provider == "base" || provider.empty()) {
84✔
274
      std::string userid;
54✔
275
      std::string hash;
54✔
276
      parse_sm2_param_string(params, userid, hash);
54✔
277
      return std::make_unique<SM2_Verification_Operation>(*this, userid, hash);
54✔
278
   }
54✔
279

280
   throw Provider_Not_Found(algo_name(), provider);
42✔
281
}
282

283
std::unique_ptr<PK_Ops::Signature> SM2_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
75✔
284
                                                                       std::string_view params,
285
                                                                       std::string_view provider) const {
286
   if(provider == "base" || provider.empty()) {
84✔
287
      std::string userid;
54✔
288
      std::string hash;
54✔
289
      parse_sm2_param_string(params, userid, hash);
54✔
290
      return std::make_unique<SM2_Signature_Operation>(*this, userid, hash);
54✔
291
   }
54✔
292

293
   throw Provider_Not_Found(algo_name(), provider);
42✔
294
}
295

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