Coverage Report

Created: 2026-01-16 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/pubkey/sm2/sm2.cpp
Line
Count
Source
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/internal/keypair.h>
13
#include <botan/internal/loadstor.h>
14
#include <botan/internal/pk_ops_impl.h>
15
16
namespace Botan {
17
18
0
std::string SM2_PublicKey::algo_name() const {
19
0
   return "SM2";
20
0
}
21
22
0
std::unique_ptr<Public_Key> SM2_PrivateKey::public_key() const {
23
0
   return std::make_unique<SM2_Signature_PublicKey>(domain(), _public_ec_point());
24
0
}
25
26
0
bool SM2_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
27
0
   if(!EC_PrivateKey::check_key(rng, strong)) {
28
0
      return false;
29
0
   }
30
31
   // SM2 has an oddity in private key generation when compared to
32
   // other EC*DSA style signature algorithms described in ISO14888-3:
33
   // the private key x MUST be in [0, q-1) instead of [0, q).
34
   //
35
   // The lower bound is already checked by the default impl
36
0
   if(private_value() >= domain().get_order() - 1) {
37
0
      return false;
38
0
   }
39
40
0
   if(!strong) {
41
0
      return true;
42
0
   }
43
44
0
   return KeyPair::signature_consistency_check(rng, *this, "user@example.com,SM3");
45
0
}
46
47
SM2_PrivateKey::SM2_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
48
0
      EC_PrivateKey(alg_id, key_bits),
49
0
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
50
0
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
Unexecuted instantiation: Botan::SM2_PrivateKey::SM2_PrivateKey(Botan::AlgorithmIdentifier const&, std::__1::span<unsigned char const, 18446744073709551615ul>)
Unexecuted instantiation: Botan::SM2_PrivateKey::SM2_PrivateKey(Botan::AlgorithmIdentifier const&, std::__1::span<unsigned char const, 18446744073709551615ul>)
51
52
SM2_PrivateKey::SM2_PrivateKey(EC_Group group, EC_Scalar x) :
53
0
      EC_PrivateKey(std::move(group), std::move(x)),
54
0
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
55
0
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
Unexecuted instantiation: Botan::SM2_PrivateKey::SM2_PrivateKey(Botan::EC_Group, Botan::EC_Scalar)
Unexecuted instantiation: Botan::SM2_PrivateKey::SM2_PrivateKey(Botan::EC_Group, Botan::EC_Scalar)
56
57
SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group) :
58
0
      EC_PrivateKey(rng, std::move(group)),
59
0
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
60
0
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
Unexecuted instantiation: Botan::SM2_PrivateKey::SM2_PrivateKey(Botan::RandomNumberGenerator&, Botan::EC_Group)
Unexecuted instantiation: Botan::SM2_PrivateKey::SM2_PrivateKey(Botan::RandomNumberGenerator&, Botan::EC_Group)
61
62
SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group, const BigInt& x) :
63
0
      EC_PrivateKey(rng, std::move(group), x),
64
0
      m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
65
0
      m_da_inv_legacy(m_da_inv.to_bigint()) {}
Unexecuted instantiation: Botan::SM2_PrivateKey::SM2_PrivateKey(Botan::RandomNumberGenerator&, Botan::EC_Group, Botan::BigInt const&)
Unexecuted instantiation: Botan::SM2_PrivateKey::SM2_PrivateKey(Botan::RandomNumberGenerator&, Botan::EC_Group, Botan::BigInt const&)
66
67
std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
68
                                    std::string_view user_id,
69
                                    const EC_Group& group,
70
0
                                    const EC_AffinePoint& pubkey) {
71
0
   if(user_id.size() >= 8192) {
72
0
      throw Invalid_Argument("SM2 user id too long to represent");
73
0
   }
74
75
0
   const uint16_t uid_len = static_cast<uint16_t>(8 * user_id.size());
76
77
0
   hash.update(get_byte<0>(uid_len));
78
0
   hash.update(get_byte<1>(uid_len));
79
0
   hash.update(user_id);
80
81
0
   const size_t p_bytes = group.get_p_bytes();
82
83
0
   hash.update(group.get_a().serialize(p_bytes));
84
0
   hash.update(group.get_b().serialize(p_bytes));
85
0
   hash.update(group.get_g_x().serialize(p_bytes));
86
0
   hash.update(group.get_g_y().serialize(p_bytes));
87
0
   hash.update(pubkey.xy_bytes());
88
89
0
   return hash.final<std::vector<uint8_t>>();
90
0
}
91
92
namespace {
93
94
/**
95
* SM2 signature operation
96
*/
97
class SM2_Signature_Operation final : public PK_Ops::Signature {
98
   public:
99
      SM2_Signature_Operation(const SM2_PrivateKey& sm2, std::string_view ident, std::string_view hash) :
100
0
            m_group(sm2.domain()), m_x(sm2._private_key()), m_da_inv(sm2._get_da_inv()) {
101
0
         if(hash == "Raw") {
102
            // m_hash is null, m_za is empty
103
0
         } else {
104
0
            m_hash = HashFunction::create_or_throw(hash);
105
            // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
106
0
            m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
107
0
            m_hash->update(m_za);
108
0
         }
109
0
      }
110
111
0
      size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
112
113
0
      void update(std::span<const uint8_t> input) override {
114
0
         if(m_hash) {
115
0
            m_hash->update(input);
116
0
         } else {
117
0
            m_digest.insert(m_digest.end(), input.begin(), input.end());
118
0
         }
119
0
      }
120
121
      std::vector<uint8_t> sign(RandomNumberGenerator& rng) override;
122
123
0
      std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
124
125
   private:
126
      const EC_Group m_group;
127
      const EC_Scalar m_x;
128
      const EC_Scalar m_da_inv;
129
130
      std::vector<uint8_t> m_za;
131
      secure_vector<uint8_t> m_digest;
132
      std::unique_ptr<HashFunction> m_hash;
133
};
134
135
0
std::vector<uint8_t> SM2_Signature_Operation::sign(RandomNumberGenerator& rng) {
136
0
   const auto e = [&]() {
137
0
      if(m_hash) {
138
0
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
139
         // prepend ZA for next signature if any
140
0
         m_hash->update(m_za);
141
0
         return ie;
142
0
      } else {
143
0
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
144
0
         m_digest.clear();
145
0
         return ie;
146
0
      }
147
0
   }();
148
149
0
   const auto k = EC_Scalar::random(m_group, rng);
150
151
0
   const auto r = EC_Scalar::gk_x_mod_order(k, rng) + e;
152
0
   const auto s = (k - r * m_x) * m_da_inv;
153
154
0
   return EC_Scalar::serialize_pair(r, s);
155
0
}
156
157
/**
158
* SM2 verification operation
159
*/
160
class SM2_Verification_Operation final : public PK_Ops::Verification {
161
   public:
162
      SM2_Verification_Operation(const SM2_PublicKey& sm2, std::string_view ident, std::string_view hash) :
163
0
            m_group(sm2.domain()), m_gy_mul(sm2._public_ec_point()) {
164
0
         if(hash == "Raw") {
165
            // m_hash is null, m_za is empty
166
0
         } else {
167
0
            m_hash = HashFunction::create_or_throw(hash);
168
            // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
169
0
            m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
170
0
            m_hash->update(m_za);
171
0
         }
172
0
      }
173
174
0
      void update(std::span<const uint8_t> input) override {
175
0
         if(m_hash) {
176
0
            m_hash->update(input);
177
0
         } else {
178
0
            m_digest.insert(m_digest.end(), input.begin(), input.end());
179
0
         }
180
0
      }
181
182
      bool is_valid_signature(std::span<const uint8_t> sig) override;
183
184
0
      std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
185
186
   private:
187
      const EC_Group m_group;
188
      const EC_Group::Mul2Table m_gy_mul;
189
      secure_vector<uint8_t> m_digest;
190
      std::vector<uint8_t> m_za;
191
      std::unique_ptr<HashFunction> m_hash;
192
};
193
194
0
bool SM2_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
195
0
   const auto e = [&]() {
196
0
      if(m_hash) {
197
0
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
198
         // prepend ZA for next signature if any
199
0
         m_hash->update(m_za);
200
0
         return ie;
201
0
      } else {
202
0
         auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
203
0
         m_digest.clear();
204
0
         return ie;
205
0
      }
206
0
   }();
207
208
0
   if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
209
0
      const auto& [r, s] = rs.value();
210
211
0
      if(r.is_nonzero() && s.is_nonzero()) {
212
0
         const auto t = r + s;
213
0
         if(t.is_nonzero()) {
214
            // Check if r - e = x_coord(g*s + y*t) % n
215
0
            return m_gy_mul.mul2_vartime_x_mod_order_eq(r - e, s, t);
216
0
         }
217
0
      }
218
0
   }
219
0
   return false;
220
0
}
221
222
0
void parse_sm2_param_string(std::string_view params, std::string& userid, std::string& hash) {
223
   // GM/T 0009-2012 specifies this as the default userid
224
0
   const std::string default_userid = "1234567812345678";
225
226
   // defaults:
227
0
   userid = default_userid;
228
0
   hash = "SM3";
229
230
   /*
231
   * SM2 parameters have the following possible formats:
232
   * Ident [since 2.2.0]
233
   * Ident,Hash [since 2.3.0]
234
   */
235
236
0
   auto comma = params.find(',');
237
0
   if(comma == std::string::npos) {
238
0
      userid = params;
239
0
   } else {
240
0
      userid = params.substr(0, comma);
241
0
      hash = params.substr(comma + 1, std::string::npos);
242
0
   }
243
0
}
244
245
}  // namespace
246
247
0
std::unique_ptr<Private_Key> SM2_PublicKey::generate_another(RandomNumberGenerator& rng) const {
248
0
   return std::make_unique<SM2_PrivateKey>(rng, domain());
249
0
}
250
251
std::unique_ptr<PK_Ops::Verification> SM2_PublicKey::create_verification_op(std::string_view params,
252
0
                                                                            std::string_view provider) const {
253
0
   if(provider == "base" || provider.empty()) {
254
0
      std::string userid;
255
0
      std::string hash;
256
0
      parse_sm2_param_string(params, userid, hash);
257
0
      return std::make_unique<SM2_Verification_Operation>(*this, userid, hash);
258
0
   }
259
260
0
   throw Provider_Not_Found(algo_name(), provider);
261
0
}
262
263
std::unique_ptr<PK_Ops::Signature> SM2_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
264
                                                                       std::string_view params,
265
0
                                                                       std::string_view provider) const {
266
0
   if(provider == "base" || provider.empty()) {
267
0
      std::string userid;
268
0
      std::string hash;
269
0
      parse_sm2_param_string(params, userid, hash);
270
0
      return std::make_unique<SM2_Signature_Operation>(*this, userid, hash);
271
0
   }
272
273
0
   throw Provider_Not_Found(algo_name(), provider);
274
0
}
275
276
}  // namespace Botan