Botan 3.11.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/keypair.h>
14#include <botan/internal/loadstor.h>
15#include <botan/internal/pk_ops_impl.h>
16
17namespace Botan {
18
19std::string SM2_PublicKey::algo_name() const {
20 return "SM2";
21}
22
24 return domain().get_order_bytes();
25}
26
27std::unique_ptr<Public_Key> SM2_PrivateKey::public_key() const {
28 return std::make_unique<SM2_Signature_PublicKey>(domain(), _public_ec_point());
29}
30
31bool SM2_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) const {
32 if(!EC_PrivateKey::check_key(rng, strong)) {
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) {
42 return false;
43 }
44
45 if(!strong) {
46 return true;
47 }
48
49 return KeyPair::signature_consistency_check(rng, *this, "user@example.com,SM3");
50}
51
52SM2_PrivateKey::SM2_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) :
53 EC_PrivateKey(alg_id, key_bits),
54 m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
55 m_da_inv_legacy(m_da_inv.to_bigint()) {}
56
58 EC_PrivateKey(group, x),
59 m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
60 m_da_inv_legacy(m_da_inv.to_bigint()) {}
61
63 EC_PrivateKey(rng, group),
64 m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
65 m_da_inv_legacy(m_da_inv.to_bigint()) {}
66
68 EC_PrivateKey(rng, group, x),
69 m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()),
70 m_da_inv_legacy(m_da_inv.to_bigint()) {}
71
72#if defined(BOTAN_HAS_LEGACY_EC_POINT)
73std::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
82std::vector<uint8_t> sm2_compute_za(HashFunction& hash,
83 std::string_view user_id,
84 const EC_Group& group,
85 const EC_AffinePoint& pubkey) {
86 if(user_id.size() >= 8192) {
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());
91
92 hash.update(get_byte<0>(uid_len));
93 hash.update(get_byte<1>(uid_len));
94 hash.update(user_id);
95
96 const size_t p_bytes = group.get_p_bytes();
97
98 hash.update(group.get_a().serialize(p_bytes));
99 hash.update(group.get_b().serialize(p_bytes));
100 hash.update(group.get_g_x().serialize(p_bytes));
101 hash.update(group.get_g_y().serialize(p_bytes));
102 hash.update(pubkey.xy_bytes());
103
104 return hash.final<std::vector<uint8_t>>();
105}
106
107namespace {
108
109/**
110* SM2 signature operation
111*/
112class 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) :
115 m_group(sm2.domain()), m_x(sm2._private_key()), m_da_inv(sm2._get_da_inv()) {
116 if(hash == "Raw") {
117 // m_hash is null, m_za is empty
118 } else {
119 m_hash = HashFunction::create_or_throw(hash);
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());
122 m_hash->update(m_za);
123 }
124 }
125
126 size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
127
128 void update(std::span<const uint8_t> input) override {
129 if(m_hash) {
130 m_hash->update(input);
131 } else {
132 m_digest.insert(m_digest.end(), input.begin(), input.end());
133 }
134 }
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"; }
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
150std::vector<uint8_t> SM2_Signature_Operation::sign(RandomNumberGenerator& rng) {
151 const auto e = [&]() {
152 if(m_hash) {
153 auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
154 // prepend ZA for next signature if any
155 m_hash->update(m_za);
156 return ie;
157 } else {
158 auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
159 m_digest.clear();
160 return ie;
161 }
162 }();
163
164 const auto k = EC_Scalar::random(m_group, rng);
165
166 const auto r = EC_Scalar::gk_x_mod_order(k, rng) + e;
167 const auto s = (k - r * m_x) * m_da_inv;
168
169 return EC_Scalar::serialize_pair(r, s);
170}
171
172/**
173* SM2 verification operation
174*/
175class SM2_Verification_Operation final : public PK_Ops::Verification {
176 public:
177 SM2_Verification_Operation(const SM2_PublicKey& sm2, std::string_view ident, std::string_view hash) :
178 m_group(sm2.domain()), m_gy_mul(sm2._public_ec_point()) {
179 if(hash == "Raw") {
180 // m_hash is null, m_za is empty
181 } else {
182 m_hash = HashFunction::create_or_throw(hash);
183 // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
184 m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point());
185 m_hash->update(m_za);
186 }
187 }
188
189 void update(std::span<const uint8_t> input) override {
190 if(m_hash) {
191 m_hash->update(input);
192 } else {
193 m_digest.insert(m_digest.end(), input.begin(), input.end());
194 }
195 }
196
197 bool is_valid_signature(std::span<const uint8_t> sig) override;
198
199 std::string hash_function() const override { return m_hash ? m_hash->name() : "Raw"; }
200
201 private:
202 const EC_Group m_group;
203 const EC_Group::Mul2Table m_gy_mul;
204 secure_vector<uint8_t> m_digest;
205 std::vector<uint8_t> m_za;
206 std::unique_ptr<HashFunction> m_hash;
207};
208
209bool SM2_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
210 const auto e = [&]() {
211 if(m_hash) {
212 auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_hash->final());
213 // prepend ZA for next signature if any
214 m_hash->update(m_za);
215 return ie;
216 } else {
217 auto ie = EC_Scalar::from_bytes_mod_order(m_group, m_digest);
218 m_digest.clear();
219 return ie;
220 }
221 }();
222
223 if(auto rs = EC_Scalar::deserialize_pair(m_group, sig)) {
224 const auto& [r, s] = rs.value();
225
226 if(r.is_nonzero() && s.is_nonzero()) {
227 const auto t = r + s;
228 if(t.is_nonzero()) {
229 // Check if r - e = x_coord(g*s + y*t) % n
230 return m_gy_mul.mul2_vartime_x_mod_order_eq(r - e, s, t);
231 }
232 }
233 }
234 return false;
235}
236
237void parse_sm2_param_string(std::string_view params, std::string& userid, std::string& hash) {
238 // GM/T 0009-2012 specifies this as the default userid
239 const std::string default_userid = "1234567812345678";
240
241 // defaults:
242 userid = default_userid;
243 hash = "SM3";
244
245 /*
246 * SM2 parameters have the following possible formats:
247 * Ident [since 2.2.0]
248 * Ident,Hash [since 2.3.0]
249 */
250
251 auto comma = params.find(',');
252 if(comma == std::string::npos) {
253 userid = params;
254 } else {
255 userid = params.substr(0, comma);
256 hash = params.substr(comma + 1, std::string::npos);
257 }
258}
259
260} // namespace
261
262std::unique_ptr<Private_Key> SM2_PublicKey::generate_another(RandomNumberGenerator& rng) const {
263 return std::make_unique<SM2_PrivateKey>(rng, domain());
264}
265
266std::unique_ptr<PK_Ops::Verification> SM2_PublicKey::create_verification_op(std::string_view params,
267 std::string_view provider) const {
268 if(provider == "base" || provider.empty()) {
269 std::string userid;
270 std::string hash;
271 parse_sm2_param_string(params, userid, hash);
272 return std::make_unique<SM2_Verification_Operation>(*this, userid, hash);
273 }
274
275 throw Provider_Not_Found(algo_name(), provider);
276}
277
278std::unique_ptr<PK_Ops::Signature> SM2_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
279 std::string_view params,
280 std::string_view provider) const {
281 if(provider == "base" || provider.empty()) {
282 std::string userid;
283 std::string hash;
284 parse_sm2_param_string(params, userid, hash);
285 return std::make_unique<SM2_Signature_Operation>(*this, userid, hash);
286 }
287
288 throw Provider_Not_Found(algo_name(), provider);
289}
290
291} // namespace Botan
T serialize(size_t len) const
Definition bigint.h:727
void update(const uint8_t in[], size_t length)
Definition buf_comp.h:34
void final(uint8_t out[])
Definition buf_comp.h:69
bool mul2_vartime_x_mod_order_eq(const EC_Scalar &v, const EC_Scalar &x, const EC_Scalar &y) const
Definition ec_group.cpp:843
const BigInt & get_b() const
Definition ec_group.cpp:617
const BigInt & get_a() const
Definition ec_group.cpp:613
const BigInt & get_g_y() const
Definition ec_group.cpp:665
const BigInt & get_g_x() const
Definition ec_group.cpp:661
size_t get_p_bytes() const
Definition ec_group.cpp:597
size_t get_order_bytes() const
Definition ec_group.cpp:605
const EC_Scalar & _private_key() const
Definition ecc_key.cpp:123
const BigInt & private_value() const
Definition ecc_key.cpp:118
EC_PrivateKey(const EC_PrivateKey &other)=default
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition ecc_key.cpp:201
const EC_Group & domain() const
Definition ecc_key.cpp:64
const EC_AffinePoint & _public_ec_point() const
Definition ecc_key.cpp:76
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:308
std::unique_ptr< Public_Key > public_key() const override
Definition sm2.cpp:27
std::unique_ptr< PK_Ops::Signature > create_signature_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition sm2.cpp:278
SM2_PrivateKey(const AlgorithmIdentifier &alg_id, std::span< const uint8_t > key_bits)
Definition sm2.cpp:52
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition sm2.cpp:31
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
Definition sm2.cpp:266
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
Definition sm2.cpp:262
std::optional< size_t > _signature_element_size_for_DER_encoding() const override
Definition sm2.cpp:23
std::string algo_name() const override
Definition sm2.cpp:19
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::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:68
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:82