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