1use getrandom::SysRng;
20use sm2::SecretKey;
21use sm2::dsa::signature::{Signer as _, Verifier as _};
22use sm2::dsa::{Signature, SigningKey, VerifyingKey};
23use sm2::elliptic_curve::Generate;
24use sm2::elliptic_curve::sec1::ToSec1Point;
25use sm2::pke::{DecryptingKey, EncryptingKey};
26
27use crate::contract::{AsymmetricCipher, Identified, Signer};
28use crate::error::{Error, Result};
29use crate::internal::{hex_decode, hex_encode};
30
31const LABEL: &str = "SM2";
32
33pub const DEFAULT_DIST_ID: &str = "1234567812345678";
39
40pub use crate::asymmetric::KeyPairHex;
45
46#[derive(Debug, Default, Clone, Copy)]
48pub struct Sm2Service;
49
50impl Sm2Service {
51 pub fn encrypt(plaintext: &[u8], public_key_hex: &str) -> Result<String> {
53 let encrypting_key = parse_public_key(public_key_hex)?;
54 let ciphertext = encrypting_key
55 .encrypt(&mut SysRng, plaintext)
56 .map_err(|_| Error::EncryptionFailed { label: LABEL })?;
57 Ok(hex_encode(&ciphertext))
58 }
59
60 pub fn decrypt(ciphertext_hex: &str, private_key_hex: &str) -> Result<Vec<u8>> {
62 let decrypting_key = DecryptingKey::new(parse_private_key(private_key_hex)?);
63 let ciphertext =
64 hex_decode(ciphertext_hex).map_err(|_| Error::DecryptionFailed { label: LABEL })?;
65 decrypting_key
66 .decrypt(&ciphertext)
67 .map_err(|_| Error::DecryptionFailed { label: LABEL })
68 }
69
70 pub fn generate_key_pair_hex() -> Result<KeyPairHex> {
72 let secret_key = SecretKey::try_generate_from_rng(&mut SysRng)
73 .map_err(|_| Error::EncryptionFailed { label: LABEL })?;
74 let public_key = secret_key.public_key();
75
76 Ok(KeyPairHex {
77 private_key_hex: hex_encode(secret_key.to_bytes().as_slice()),
78 public_key_hex: hex_encode(public_key.to_sec1_point(false).as_bytes()),
79 })
80 }
81
82 pub fn sign(message: &[u8], private_key_hex: &str) -> Result<String> {
90 let signing_key = SigningKey::new(DEFAULT_DIST_ID, &parse_private_key(private_key_hex)?)
91 .map_err(|_| Error::SignFailed { label: LABEL })?;
92 let signature: Signature = signing_key
93 .try_sign(message)
94 .map_err(|_| Error::SignFailed { label: LABEL })?;
95 Ok(hex_encode(signature.to_bytes().as_slice()))
96 }
97
98 pub fn verify(message: &[u8], signature_hex: &str, public_key_hex: &str) -> Result<()> {
104 let fail = || Error::VerificationFailed { label: LABEL };
105 let sec1 = sec1_public_key_bytes(public_key_hex).map_err(|_| fail())?;
106 let verifying_key =
107 VerifyingKey::from_sec1_bytes(DEFAULT_DIST_ID, &sec1).map_err(|_| fail())?;
108 let signature_bytes = hex_decode(signature_hex).map_err(|_| fail())?;
109 let signature = Signature::from_slice(&signature_bytes).map_err(|_| fail())?;
110 verifying_key
111 .verify(message, &signature)
112 .map_err(|_| fail())
113 }
114}
115
116#[derive(Debug, Default, Clone, Copy)]
118pub struct Sm2AsymmetricCipher;
119
120impl Sm2AsymmetricCipher {
121 pub const IDENTIFIER: &'static str = "sm2";
122
123 pub fn new() -> Self {
124 Self
125 }
126}
127
128impl Identified for Sm2AsymmetricCipher {
129 fn identifier(&self) -> &str {
130 Self::IDENTIFIER
131 }
132}
133
134impl AsymmetricCipher for Sm2AsymmetricCipher {
135 fn encrypt(&self, plaintext: &[u8], public_key_hex: &str) -> Result<String> {
136 Sm2Service::encrypt(plaintext, public_key_hex)
137 }
138
139 fn decrypt(&self, ciphertext_hex: &str, private_key_hex: &str) -> Result<Vec<u8>> {
140 Sm2Service::decrypt(ciphertext_hex, private_key_hex)
141 }
142}
143
144#[derive(Debug, Default, Clone, Copy)]
146pub struct Sm2Signer;
147
148impl Sm2Signer {
149 pub const IDENTIFIER: &'static str = "sm2";
150
151 pub fn new() -> Self {
152 Self
153 }
154}
155
156impl Identified for Sm2Signer {
157 fn identifier(&self) -> &str {
158 Self::IDENTIFIER
159 }
160}
161
162impl Signer for Sm2Signer {
163 fn sign(&self, message: &[u8], private_key_hex: &str) -> Result<String> {
164 Sm2Service::sign(message, private_key_hex)
165 }
166
167 fn verify(&self, message: &[u8], signature_hex: &str, public_key_hex: &str) -> Result<()> {
168 Sm2Service::verify(message, signature_hex, public_key_hex)
169 }
170}
171
172fn sec1_public_key_bytes(public_key_hex: &str) -> Result<Vec<u8>> {
175 let bytes = hex_decode(public_key_hex).map_err(|_| Error::InvalidKey { label: LABEL })?;
176
177 match bytes.len() {
178 65 | 33 => Ok(bytes),
179 64 => {
180 let mut normalized = Vec::with_capacity(65);
182 normalized.push(0x04);
183 normalized.extend_from_slice(&bytes);
184 Ok(normalized)
185 }
186 _ => Err(Error::InvalidKey { label: LABEL }),
187 }
188}
189
190fn parse_public_key(public_key_hex: &str) -> Result<EncryptingKey> {
191 let sec1 = sec1_public_key_bytes(public_key_hex)?;
192 EncryptingKey::from_sec1_bytes(&sec1).map_err(|_| Error::InvalidKey { label: LABEL })
193}
194
195fn parse_private_key(private_key_hex: &str) -> Result<SecretKey> {
197 let bytes = hex_decode(private_key_hex).map_err(|_| Error::InvalidKey { label: LABEL })?;
198 SecretKey::from_slice(&bytes).map_err(|_| Error::InvalidKey { label: LABEL })
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn keypair_roundtrip_and_hex_shapes() {
207 let pair = Sm2Service::generate_key_pair_hex().unwrap();
208 assert_eq!(pair.private_key_hex.len(), 64);
209 assert_eq!(pair.public_key_hex.len(), 130);
210 assert!(pair.public_key_hex.starts_with("04"));
211
212 let ciphertext =
213 Sm2Service::encrypt("国密数据 🎉".as_bytes(), &pair.public_key_hex).unwrap();
214 let plaintext = Sm2Service::decrypt(&ciphertext, &pair.private_key_hex).unwrap();
215 assert_eq!(plaintext, "国密数据 🎉".as_bytes());
216 }
217
218 #[test]
219 fn bare_xy_public_key_is_accepted_too() {
220 let pair = Sm2Service::generate_key_pair_hex().unwrap();
221 let bare = &pair.public_key_hex[2..]; let ciphertext = Sm2Service::encrypt(b"payload", bare).unwrap();
224 let plaintext = Sm2Service::decrypt(&ciphertext, &pair.private_key_hex).unwrap();
225 assert_eq!(plaintext, b"payload");
226 }
227
228 #[test]
229 fn wrong_private_key_or_tampered_ciphertext_fails() {
230 let pair = Sm2Service::generate_key_pair_hex().unwrap();
231 let other = Sm2Service::generate_key_pair_hex().unwrap();
232 let ciphertext = Sm2Service::encrypt(b"secret", &pair.public_key_hex).unwrap();
233
234 assert_eq!(
235 Sm2Service::decrypt(&ciphertext, &other.private_key_hex).unwrap_err(),
236 Error::DecryptionFailed { label: LABEL }
237 );
238
239 let mut tampered = ciphertext.clone();
240 let last = tampered.len() - 1;
241 let flipped = if tampered.as_bytes()[last] == b'0' {
242 '1'
243 } else {
244 '0'
245 };
246 tampered.replace_range(last.., &flipped.to_string());
247 assert_eq!(
248 Sm2Service::decrypt(&tampered, &pair.private_key_hex).unwrap_err(),
249 Error::DecryptionFailed { label: LABEL }
250 );
251 }
252
253 #[test]
254 fn malformed_keys_are_rejected() {
255 let pair = Sm2Service::generate_key_pair_hex().unwrap();
256
257 assert_eq!(
258 Sm2Service::decrypt("00", "zz").unwrap_err(),
259 Error::InvalidKey { label: LABEL }
260 );
261 assert_eq!(
262 Sm2Service::encrypt(b"x", "abcd").unwrap_err(),
263 Error::InvalidKey { label: LABEL }
264 );
265 let mut longer = pair.private_key_hex.clone();
267 longer.push('a');
268 assert_eq!(
269 Sm2Service::decrypt("00", &longer).unwrap_err(),
270 Error::InvalidKey { label: LABEL }
271 );
272 }
273
274 #[test]
275 fn cipher_trait_matches_service() {
276 let cipher = Sm2AsymmetricCipher::new();
277 assert_eq!(cipher.identifier(), "sm2");
278
279 let pair = Sm2Service::generate_key_pair_hex().unwrap();
280 let ciphertext = cipher.encrypt(b"via trait", &pair.public_key_hex).unwrap();
281 assert_eq!(
282 cipher.decrypt(&ciphertext, &pair.private_key_hex).unwrap(),
283 b"via trait"
284 );
285 }
286
287 #[test]
288 fn sign_verify_roundtrip_and_signature_shape() {
289 let pair = Sm2Service::generate_key_pair_hex().unwrap();
290 let signature = Sm2Service::sign("国密签名 🎉".as_bytes(), &pair.private_key_hex).unwrap();
291
292 assert_eq!(signature.len(), 128); Sm2Service::verify("国密签名 🎉".as_bytes(), &signature, &pair.public_key_hex).unwrap();
294 }
295
296 #[test]
297 fn signature_is_deterministic() {
298 let pair = Sm2Service::generate_key_pair_hex().unwrap();
300 let first = Sm2Service::sign(b"same message", &pair.private_key_hex).unwrap();
301 let second = Sm2Service::sign(b"same message", &pair.private_key_hex).unwrap();
302 assert_eq!(first, second);
303 }
304
305 #[test]
306 fn verify_rejects_wrong_input_forms() {
307 let failed = Error::VerificationFailed { label: LABEL };
308 let pair = Sm2Service::generate_key_pair_hex().unwrap();
309 let other = Sm2Service::generate_key_pair_hex().unwrap();
310 let signature = Sm2Service::sign(b"message", &pair.private_key_hex).unwrap();
311
312 assert_eq!(
314 Sm2Service::verify(b"messagE", &signature, &pair.public_key_hex).unwrap_err(),
315 failed
316 );
317 assert_eq!(
319 Sm2Service::verify(b"message", &signature, &other.public_key_hex).unwrap_err(),
320 failed
321 );
322 let mut tampered = signature.clone();
324 let last = tampered.len() - 1;
325 let flipped = if tampered.as_bytes()[last] == b'0' {
326 '1'
327 } else {
328 '0'
329 };
330 tampered.replace_range(last.., &flipped.to_string());
331 assert_eq!(
332 Sm2Service::verify(b"message", &tampered, &pair.public_key_hex).unwrap_err(),
333 failed
334 );
335 assert_eq!(
337 Sm2Service::verify(b"message", &signature, "zz").unwrap_err(),
338 failed
339 );
340 assert_eq!(
341 Sm2Service::verify(b"message", "not-hex", &pair.public_key_hex).unwrap_err(),
342 failed
343 );
344 assert_eq!(
345 Sm2Service::verify(b"message", "00", &pair.public_key_hex).unwrap_err(),
346 failed
347 );
348 }
349
350 #[test]
351 fn bare_xy_public_key_verifies_too() {
352 let pair = Sm2Service::generate_key_pair_hex().unwrap();
353 let bare = &pair.public_key_hex[2..]; let signature = Sm2Service::sign(b"payload", &pair.private_key_hex).unwrap();
356 Sm2Service::verify(b"payload", &signature, bare).unwrap();
357 }
358
359 #[test]
361 fn compressed_public_key_verifies_too() {
362 use sm2::elliptic_curve::sec1::ToSec1Point;
363
364 let pair = Sm2Service::generate_key_pair_hex().unwrap();
365 let secret = sm2::SecretKey::from_slice(
366 &crate::internal::hex_decode(&pair.private_key_hex).unwrap(),
367 )
368 .unwrap();
369 let compressed_hex =
370 crate::internal::hex_encode(secret.public_key().to_sec1_point(true).as_bytes());
371 assert_eq!(compressed_hex.len(), 66);
372
373 let signature = Sm2Service::sign(b"payload", &pair.private_key_hex).unwrap();
374 Sm2Service::verify(b"payload", &signature, &compressed_hex).unwrap();
375 }
376
377 #[test]
380 fn signature_is_accepted_by_raw_sm2_crate() {
381 let pair = Sm2Service::generate_key_pair_hex().unwrap();
382 let signature_hex = Sm2Service::sign(b"cross-layer", &pair.private_key_hex).unwrap();
383
384 let sec1 = crate::internal::hex_decode(&pair.public_key_hex).unwrap();
385 let verifying_key =
386 sm2::dsa::VerifyingKey::from_sec1_bytes(DEFAULT_DIST_ID, &sec1).unwrap();
387 let sig_bytes = crate::internal::hex_decode(&signature_hex).unwrap();
388 let signature = sm2::dsa::Signature::from_slice(&sig_bytes).unwrap();
389 verifying_key.verify(b"cross-layer", &signature).unwrap();
390 }
391
392 #[test]
393 fn sign_rejects_malformed_private_key() {
394 assert_eq!(
395 Sm2Service::sign(b"x", "zz").unwrap_err(),
396 Error::InvalidKey { label: LABEL }
397 );
398 assert_eq!(
399 Sm2Service::sign(b"x", "abcd").unwrap_err(),
400 Error::InvalidKey { label: LABEL }
401 );
402 }
403
404 #[test]
405 fn signer_trait_matches_service() {
406 let signer = Sm2Signer::new();
407 assert_eq!(signer.identifier(), "sm2");
408
409 let pair = Sm2Service::generate_key_pair_hex().unwrap();
410 let signature = signer.sign(b"via trait", &pair.private_key_hex).unwrap();
411 signer
412 .verify(b"via trait", &signature, &pair.public_key_hex)
413 .unwrap();
414 assert_eq!(
415 signer.verify(b"other", &signature, &pair.public_key_hex),
416 Err(Error::VerificationFailed { label: LABEL })
417 );
418 }
419
420 #[test]
433 fn verify_accepts_gmt0003_annex_a_openssl_signature() {
434 const PUBLIC_KEY: &str = concat!(
435 "04", "09F9DF311E5421A150DD7D161E4BC5C672179FAD1833FC076BB08FF356F35020", "CCEA490CE26775A52DC6EA718CC1AA600AED05FBF35E084A6632F6072DA9AD13", );
439 const SIGNATURE: &str = concat!(
440 "F5A03B0648D2C4630EEAC513E1BB81A15944DA3827D5B74143AC7EACEEE720B3", "B1B6AA29DF212FD8763182BC0D421CA1BB9038FD1F7F42D4840B69C485BBC1AA", );
443
444 Sm2Service::verify(b"message digest", SIGNATURE, PUBLIC_KEY)
445 .expect("GM/T 0003.5 附录 A 的 r‖s 应通过验签");
446 }
447}