Botan 3.13.0
Crypto and TLS for C&
sm2.cpp
Go to the documentation of this file.
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/fmt.h>
14#include <botan/internal/keypair.h>
15#include <botan/internal/loadstor.h>
16#include <botan/internal/pk_ops_impl.h>
17
18namespace Botan {
19
20std::string SM2_PublicKey::algo_name() const {
21 return "SM2";
22}
23
24namespace {
25
26const AlgorithmIdentifier& assert_sm2_algorithm_identifier(const AlgorithmIdentifier& alg_id) {
27 const auto alg_name = alg_id.oid().registered_name();
28 // OpenSSL uses ECDSA's OID for SM2
29 if(alg_name != "SM2" && alg_name != "SM2_Enc" && alg_name != "ECDSA") {
30 throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier OID {} in association with SM2 key", alg_id.oid()));
31 }
32
33 return alg_id; // NOLINT(*-return-const-ref-from-parameter)
34}
35
36} // namespace
37
38SM2_PublicKey::SM2_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
39 EC_PublicKey(assert_sm2_algorithm_identifier(alg_id), key_bits) {}
40
42 return domain().get_order_bytes();
43}
44
45std::unique_ptr<Public_Key> SM2_PrivateKey::public_key() const {
46 return std::make_unique<SM2_Signature_PublicKey>(domain(), _public_ec_point());
47}
48
49bool SM2_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
50 if(!EC_PrivateKey::check_key(rng, strong)) {
51 return false;
52 }
53
54 // SM2 has an oddity in private key generation when compared to
55 // other EC*DSA style signature algorithms described in ISO14888-3:
56 // the private key x MUST be in [0, q-1) instead of [0, q).
57 //
58 // The lower bound is already checked by the default impl
59 if(private_value() >= domain().get_order() - 1) {
60 return false;
61 }
62
63 if(!strong) {
64 return true;
65 }
66
67 return KeyPair::signature_consistency_check(rng, *this, "user@example.com,SM3");
68}
69
70SM2_PrivateKey::SM2_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
71 EC_PrivateKey(assert_sm2_algorithm_identifier(alg_id), key_bits),
72 m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
73 m_da_inv_legacy(m_da_inv.to_bigint()) {
74 if(m_da_inv.is_zero()) {
75 throw Decoding_Error("SM2 private key cannot equal n-1");
76 }
77}
78
80 EC_PrivateKey(group, x),
81 m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
82 m_da_inv_legacy(m_da_inv.to_bigint()) {
83 BOTAN_ARG_CHECK(m_da_inv.is_nonzero(), "SM2 private key cannot equal n-1");
84}
85
86namespace {
87
88// Avoid the (unlikely) case of random generating an invalid key of n - 1
89EC_Scalar generate_sm2_private_key(RandomNumberGenerator& rng, const EC_Group& group) {
90 const auto one = EC_Scalar::one(group);
91
92 for(;;) {
93 // EC_Scalar::random never returns zero
94 auto x = EC_Scalar::random(group, rng);
95 BOTAN_ASSERT_NOMSG(x.is_nonzero());
96 if((x + one).is_nonzero()) {
97 return x;
98 }
99 }
100}
101
102} // namespace
103
105 SM2_PrivateKey(group, generate_sm2_private_key(rng, group)) {}
106
108 EC_PrivateKey(rng, group, x),
109 m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
110 m_da_inv_legacy(m_da_inv.to_bigint()) {
111 BOTAN_ARG_CHECK(m_da_inv.is_nonzero(), "SM2 private key cannot equal n-1");
112}
113
114#if defined(BOTAN_HAS_LEGACY_EC_POINT)
115std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
116 std::string_view user_id,
117 const EC_Group& group,
118 const EC_Point& pubkey) {
119 auto apoint = EC_AffinePoint(group, pubkey);
120 return sm2_compute_za(hash, user_id, group, apoint);
121}
122#endif
123
124std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
125 std::string_view user_id,
126 const EC_Group& group,
127 const EC_AffinePoint& pubkey) {
128 if(user_id.size() >= 8192) {
129 throw Invalid_Argument("SM2 user id too long to represent");
130 }
131
132 const uint16_t uid_len = static_cast<uint16_t>(8 * user_id.size());
133
134 hash.update(get_byte<0>(uid_len));
135 hash.update(get_byte<1>(uid_len));
136 hash.update(user_id);
137
138 const size_t p_bytes = group.get_p_bytes();
139
140 hash.update(group.get_a().serialize(p_bytes));
141 hash.update(group.get_b().serialize(p_bytes));
142 hash.update(group.get_g_x().serialize(p_bytes));
143 hash.update(group.get_g_y().serialize(p_bytes));
144 hash.update(pubkey.xy_bytes());
145
146 return hash.final<std::vector<uint8_t>>();
147}
148
149namespace {
150
151/**
152* SM2 signature operation
153*/
154class SM2_Signature_Operation final : public PK_Ops::Signature {
155 public:
156 SM2_Signature_Operation(const SM2_PrivateKey& sm2, std::string_view ident, std::string_view hash) :
157 m_group(sm2.domain()), m_x(sm2._private_key()), m_da_inv(sm2._get_da_inv()) {
158 if(hash == "Raw") {
159 // m_hash is null, m_za is empty
160 } else {
161 m_hash = HashFunction::create_or_throw(hash);
162 // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
163 m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
164 m_hash->update(m_za);
165 }
166 }
167
168 size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
169
170 void update(std::span<const uint8_t> input) override {
171 if(m_hash) {
172 m_hash->update(input);
173 } else {
174 m_digest.insert(m_digest.end(), input.begin(), input.end());
175 }
176 }
177
178 std::vector<uint8_t> sign(RandomNumberGenerator& rng) override;
179
180 std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
181
182 private:
183 const EC_Group m_group;
184 const EC_Scalar m_x;
185 const EC_Scalar m_da_inv;
186
187 std::vector<uint8_t> m_za;
188 secure_vector<uint8_t> m_digest;
189 std::unique_ptr<HashFunction> m_hash;
190};
191
192std::vector<uint8_t> SM2_Signature_Operation::sign(RandomNumberGenerator& rng) {
193 const auto e = [&]() {
194 if(m_hash) {
195 auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
196 // prepend ZA for next signature if any
197 m_hash->update(m_za);
198 return ie;
199 } else {
200 auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
201 m_digest.clear();
202 return ie;
203 }
204 }();
205
206 const auto k = EC_Scalar::random(m_group, rng);
207
208 const auto r = EC_Scalar::gk_x_mod_order(k, rng) + e;
209 const auto s = (k - r * m_x) * m_da_inv;
210
211 const auto rs = r + s;
212
213 // With overwhelming probability, a bug rather than actual zero r/s
214 if(r.is_zero() || s.is_zero() || rs.is_zero()) {
215 throw Internal_Error("During SM2 signature generated zero r/s");
216 }
217
218 return EC_Scalar::serialize_pair(r, s);
219}
220
221/**
222* SM2 verification operation
223*/
224class SM2_Verification_Operation final : public PK_Ops::Verification {
225 public:
226 SM2_Verification_Operation(const SM2_PublicKey& sm2, std::string_view ident, std::string_view hash) :
227 m_group(sm2.domain()), m_gy_mul(sm2._public_ec_point()) {
228 if(hash == "Raw") {
229 // m_hash is null, m_za is empty
230 } else {
231 m_hash = HashFunction::create_or_throw(hash);
232 // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
233 m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
234 m_hash->update(m_za);
235 }
236 }
237
238 void update(std::span<const uint8_t> input) override {
239 if(m_hash) {
240 m_hash->update(input);
241 } else {
242 m_digest.insert(m_digest.end(), input.begin(), input.end());
243 }
244 }
245
246 bool is_valid_signature(std::span<const uint8_t> sig) override;
247
248 std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
249
250 private:
251 const EC_Group m_group;
252 const EC_Group::Mul2Table m_gy_mul;
253 secure_vector<uint8_t> m_digest;
254 std::vector<uint8_t> m_za;
255 std::unique_ptr<HashFunction> m_hash;
256};
257
258bool SM2_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
259 const auto e = [&]() {
260 if(m_hash) {
261 auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
262 // prepend ZA for next signature if any
263 m_hash->update(m_za);
264 return ie;
265 } else {
266 auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
267 m_digest.clear();
268 return ie;
269 }
270 }();
271
272 if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
273 const auto& [r, s] = rs.value();
274
275 if(r.is_nonzero() && s.is_nonzero()) {
276 const auto t = r + s;
277 if(t.is_nonzero()) {
278 // Check if r - e = x_coord(g*s + y*t) % n
279 return m_gy_mul.mul2_vartime_x_mod_order_eq(r - e, s, t);
280 }
281 }
282 }
283 return false;
284}
285
286std::pair<std::string, std::string> parse_sm2_param_string(std::string_view params) {
287 const std::string default_hash = "SM3";
288
289 /*
290 * SM2 parameters have the following possible formats:
291 * Ident [since 2.2.0]
292 * Ident,Hash [since 2.3.0]
293 *
294 * Historically a completely empty parameter string was treated as
295 * if the identity was empty. This probably should have instead been
296 * treated as if it was the "default userid" ("1234567812345678") but
297 * there was a bug and it wasn't.
298 *
299 * TODO(Botan4) evaluate if this should be changed
300 */
301 if(params.empty()) {
302 return std::make_pair(std::string(), default_hash);
303 }
304
305 auto comma = params.find(',');
306 if(comma == std::string::npos) {
307 return std::make_pair(std::string(params), default_hash);
308 } else {
309 const auto userid = params.substr(0, comma);
310 const auto hash = params.substr(comma + 1, std::string::npos);
311 return std::make_pair(std::string(userid), std::string(hash));
312 }
313}
314
315} // namespace
316
317std::unique_ptr<Private_Key> SM2_PublicKey::generate_another(RandomNumberGenerator& rng) const {
318 return std::make_unique<SM2_PrivateKey>(rng, domain());
319}
320
321std::unique_ptr<PK_Ops::Verification> SM2_PublicKey::create_verification_op(std::string_view params,
322 std::string_view provider) const {
323 if(provider == "base" || provider.empty()) {
324 const auto [userid, hash] = parse_sm2_param_string(params);
325 return std::make_unique<SM2_Verification_Operation>(*this, userid, hash);
326 }
327
328 throw Provider_Not_Found(algo_name(), provider);
329}
330
331std::unique_ptr<PK_Ops::Signature> SM2_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
332 std::string_view params,
333 std::string_view provider) const {
334 if(provider == "base" || provider.empty()) {
335 const auto [userid, hash] = parse_sm2_param_string(params);
336 return std::make_unique<SM2_Signature_Operation>(*this, userid, hash);
337 }
338
339 throw Provider_Not_Found(algo_name(), provider);
340}
341
342} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
const OID & oid() const
Definition asn1_obj.h:688
T serialize(size_t len) const
Definition bigint.h:790
void update(const uint8_t in[], size_t length)
Definition buf_comp.h:35
void final(uint8_t out[])
Definition buf_comp.h:97
bool mul2_vartime_x_mod_order_eq(const EC_Scalar &v, const EC_Scalar &x, const EC_Scalar &y) const
Definition ec_group.cpp:924
const BigInt & get_b() const
Definition ec_group.cpp:678
const BigInt & get_a() const
Definition ec_group.cpp:674
const BigInt & get_g_y() const
Definition ec_group.cpp:726
const BigInt & get_g_x() const
Definition ec_group.cpp:722
size_t get_p_bytes() const
Definition ec_group.cpp:658
size_t get_order_bytes() const
Definition ec_group.cpp:666
const EC_Scalar & _private_key() const
Definition ecc_key.cpp:135
const BigInt & private_value() const
Definition ecc_key.cpp:130
EC_PrivateKey(const EC_PrivateKey &other)=default
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition ecc_key.cpp:235
const EC_Group & domain() const
Definition ecc_key.cpp:76
EC_PublicKey(const EC_PublicKey &other)=default
const EC_AffinePoint & _public_ec_point() const
Definition ecc_key.cpp:88
static EC_Scalar one(const EC_Group &group)
Definition ec_scalar.cpp:68
static EC_Scalar random(const EC_Group &group, RandomNumberGenerator &rng)
Definition ec_scalar.cpp:64
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:308
std::optional< std::string > registered_name() const
Definition asn1_oid.cpp:149
std::unique_ptr< Public_Key > public_key() const override
Definition sm2.cpp:45
std::unique_ptr< PK_Ops::Signature > create_signature_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition sm2.cpp:331
SM2_PrivateKey(const AlgorithmIdentifier &alg_id, std::span< const uint8_t > key_bits)
Definition sm2.cpp:70
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition sm2.cpp:49
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
Definition sm2.cpp:321
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
Definition sm2.cpp:317
std::optional< size_t > _signature_element_size_for_DER_encoding() const override
Definition sm2.cpp:41
std::string algo_name() const override
Definition sm2.cpp:20
bool signature_consistency_check(RandomNumberGenerator &rng, const Private_Key &private_key, const Public_Key &public_key, std::string_view padding)
Definition keypair.cpp:49
constexpr uint8_t get_byte(T input)
Definition loadstor.h:79
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
std::vector< uint8_t > sm2_compute_za(HashFunction &hash, std::string_view user_id, const EC_Group &group, const EC_AffinePoint &pubkey)
Definition sm2.cpp:124