From 010d896ee2f1491eb2651390181ad6806275f60c Mon Sep 17 00:00:00 2001 From: somes <50438673+somes@users.noreply.github.com> Date: Fri, 19 Jun 2026 22:20:49 +0800 Subject: [PATCH 1/7] feat(rust): add SM2 wrapper --- wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md | 1 + wrapper/rust/wolfssl-wolfcrypt/README.md | 1 + wrapper/rust/wolfssl-wolfcrypt/build.rs | 7 + wrapper/rust/wolfssl-wolfcrypt/headers.h | 1 + wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs | 21 +++ wrapper/rust/wolfssl-wolfcrypt/src/lib.rs | 2 + wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs | 162 ++++++++++++++++++ .../rust/wolfssl-wolfcrypt/tests/test_sm2.rs | 130 ++++++++++++++ 8 files changed, 325 insertions(+) create mode 100644 wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs create mode 100644 wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs diff --git a/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md b/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md index 620bbee02fd..8a56c766a3d 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md +++ b/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md @@ -11,6 +11,7 @@ New features: - Add BLAKE2 digest module (blake2_digest) - Add BLAKE2 MAC module (blake2_mac) - Add Aes192Ccm and Aes192Gcm +- Add SM2 wrapper (wolfssl_wolfcrypt::sm2 module) - Implement Clone for HMAC types - Improve cross-compilation and bare-metal target support in build.rs diff --git a/wrapper/rust/wolfssl-wolfcrypt/README.md b/wrapper/rust/wolfssl-wolfcrypt/README.md index ad57b33ec30..96fa34158c3 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/README.md +++ b/wrapper/rust/wolfssl-wolfcrypt/README.md @@ -50,6 +50,7 @@ functionality: * PRF * RNG * RSA + * SM2 * scrypt * SHA * SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA3-224, SHA3-256, SHA3-384, diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index 646932366bf..0ac5f2e0bef 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -504,5 +504,12 @@ fn scan_cfg() -> Result<()> { check_cfg(&binding, "wc_InitShake128", "shake128"); check_cfg(&binding, "wc_InitShake256", "shake256"); + /* sm2 */ + check_cfg(&binding, "wc_ecc_sm2_make_key", "sm2"); + check_cfg(&binding, "wc_ecc_sm2_shared_secret", "sm2_dh"); + check_cfg(&binding, "wc_ecc_sm2_sign_hash", "sm2_sign"); + check_cfg(&binding, "wc_ecc_sm2_verify_hash", "sm2_verify"); + check_cfg(&binding, "wc_ecc_sm2_create_digest", "sm2_digest"); + Ok(()) } diff --git a/wrapper/rust/wolfssl-wolfcrypt/headers.h b/wrapper/rust/wolfssl-wolfcrypt/headers.h index 719c2f01f0b..ec891914da1 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/headers.h +++ b/wrapper/rust/wolfssl-wolfcrypt/headers.h @@ -22,3 +22,4 @@ #include "wolfssl/wolfcrypt/dilithium.h" #include "wolfssl/wolfcrypt/wc_mlkem.h" #include "wolfssl/wolfcrypt/wc_lms.h" +#include "wolfssl/wolfcrypt/sm2.h" diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs b/wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs index d0e3fefc793..8c3d0a12cb8 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs @@ -396,6 +396,27 @@ impl ECC { pub const FLAG_COFACTOR: i32 = sys::WC_ECC_FLAG_COFACTOR as i32; pub const FLAG_DEC_SIGN: i32 = sys::WC_ECC_FLAG_DEC_SIGN as i32; + /// Allocate and initialize an ECC key without populating key material. + pub(crate) fn new() -> Result { + Self::new_ex(None, None) + } + + /// Allocate and initialize an ECC key without populating key material, + /// using an optional heap hint and device ID. + pub(crate) fn new_ex( + heap: Option<*mut core::ffi::c_void>, + dev_id: Option, + ) -> Result { + let heap = heap.unwrap_or(core::ptr::null_mut()); + let dev_id = dev_id.unwrap_or(sys::INVALID_DEVID); + let wc_ecc_key = Self::new_ecc_key(heap, dev_id)?; + Ok(Self { + wc_ecc_key, + #[cfg(random)] + rng: None, + }) + } + /// Allocate and initialize a new `sys::ecc_key` on the C heap. fn new_ecc_key(heap: *mut core::ffi::c_void, dev_id: i32) -> Result<*mut sys::ecc_key, i32> { let key = unsafe { sys::wc_ecc_key_new(heap) }; diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs b/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs index f25e42a5745..1725e3fd6ed 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs @@ -79,6 +79,8 @@ pub mod rsa_oaep; #[cfg(feature = "signature")] pub mod rsa_pkcs1v15; pub mod sha; +#[cfg(sm2)] +pub mod sm2; #[cfg(all(feature = "password-hash", hmac, kdf_pbkdf2))] pub mod pbkdf2_password_hash; #[cfg(all(feature = "password-hash", kdf_scrypt))] diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs b/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs new file mode 100644 index 00000000000..1adc7eac752 --- /dev/null +++ b/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/*! +This module provides a Rust wrapper for wolfCrypt SM2 functionality. +*/ + +#![cfg(sm2)] + +use crate::ecc::ECC; +#[cfg(random)] +use crate::random::RNG; +use crate::sys; + +/// An SM2 key backed by a wolfCrypt ECC key. +pub struct SM2 { + key: ECC, +} + +impl SM2 { + /// SM2 key size in bytes. + pub const KEY_SIZE: usize = sys::SM2_KEY_SIZE as usize; + + /// Default SM2 certificate signature identity. + pub const CERT_SIG_ID: &'static [u8] = b"1234567812345678"; + + /// wolfCrypt hash type identifier for SM3. + pub const HASH_TYPE_SM3: u32 = sys::wc_HashType_WC_HASH_TYPE_SM3 as u32; + + /// No ECC operation flags. + pub const FLAG_NONE: i32 = ECC::FLAG_NONE; + + /// Enable the ECC cofactor flag. + pub const FLAG_COFACTOR: i32 = ECC::FLAG_COFACTOR; + + /// Enable the ECC decrypt/sign flag. + pub const FLAG_DEC_SIGN: i32 = ECC::FLAG_DEC_SIGN; + + /// Generate a new SM2 key using the supplied random number generator. + #[cfg(random)] + pub fn generate(rng: &RNG, flags: i32) -> Result { + let key = ECC::new()?; + let rc = unsafe { sys::wc_ecc_sm2_make_key(rng.wc_rng, key.wc_ecc_key, flags) }; + if rc != 0 { + return Err(rc); + } + Ok(Self { key }) + } + + /// Derive a shared secret into the caller-supplied output buffer. + #[cfg(sm2_dh)] + pub fn shared_secret(&mut self, peer: &mut SM2, out: &mut [u8]) -> Result { + let mut out_len = crate::buffer_len_to_u32(out.len())?; + let rc = unsafe { + sys::wc_ecc_sm2_shared_secret( + self.key.wc_ecc_key, + peer.key.wc_ecc_key, + out.as_mut_ptr(), + &mut out_len, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(out_len as usize) + } + + /// Create an SM2 digest for an identity and message. + #[cfg(sm2_digest)] + pub fn create_digest( + &mut self, + id: &[u8], + message: &[u8], + hash_type: u32, + out: &mut [u8], + ) -> Result<(), i32> { + let id_len = u16::try_from(id.len()).map_err(|_| sys::wolfCrypt_ErrorCodes_BUFFER_E)?; + let message_len = crate::buffer_len_to_i32(message.len())?; + let out_len = crate::buffer_len_to_i32(out.len())?; + let rc = unsafe { + sys::wc_ecc_sm2_create_digest( + id.as_ptr(), + id_len, + message.as_ptr(), + message_len, + hash_type as sys::wc_HashType, + out.as_mut_ptr(), + out_len, + self.key.wc_ecc_key, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Sign a hash with this SM2 key and return the DER signature length. + #[cfg(all(sm2_sign, random))] + pub fn sign_hash( + &mut self, + hash: &[u8], + signature: &mut [u8], + rng: &RNG, + ) -> Result { + let hash_len = crate::buffer_len_to_u32(hash.len())?; + let mut signature_len = crate::buffer_len_to_u32(signature.len())?; + let rc = unsafe { + sys::wc_ecc_sm2_sign_hash( + hash.as_ptr(), + hash_len, + signature.as_mut_ptr(), + &mut signature_len, + rng.wc_rng, + self.key.wc_ecc_key, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(signature_len as usize) + } + + /// Verify a DER-encoded SM2 signature against a hash. + #[cfg(sm2_verify)] + pub fn verify_hash(&mut self, signature: &[u8], hash: &[u8]) -> Result { + let signature_len = crate::buffer_len_to_u32(signature.len())?; + let hash_len = crate::buffer_len_to_u32(hash.len())?; + let mut valid = 0; + let rc = unsafe { + sys::wc_ecc_sm2_verify_hash( + signature.as_ptr(), + signature_len, + hash.as_ptr(), + hash_len, + &mut valid, + self.key.wc_ecc_key, + ) + }; + if rc != 0 { + return Err(rc); + } + Ok(valid != 0) + } +} diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs new file mode 100644 index 00000000000..e084ea61ab0 --- /dev/null +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs @@ -0,0 +1,130 @@ +#![cfg(sm2)] + +mod common; + +#[cfg(random)] +use wolfssl_wolfcrypt::random::RNG; +use wolfssl_wolfcrypt::sm2::SM2; + +#[test] +#[cfg(random)] +fn test_sm2_generate() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + SM2::generate(&rng, SM2::FLAG_NONE).expect("Error with generate()"); +} + +#[test] +#[cfg(all(random, sm2_digest))] +fn test_sm2_create_digest() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); + let mut digest = [0u8; 32]; + + key.create_digest( + SM2::CERT_SIG_ID, + b"message digest", + SM2::HASH_TYPE_SM3, + &mut digest, + ) + .expect("Error creating SM2 digest"); + + assert_ne!(digest, [0u8; 32]); +} + +#[test] +#[cfg(all(random, sm2_digest, sm2_sign, sm2_verify))] +fn test_sm2_sign_and_verify() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); + let mut digest = [0u8; 32]; + key.create_digest( + SM2::CERT_SIG_ID, + b"message digest", + SM2::HASH_TYPE_SM3, + &mut digest, + ) + .expect("Error creating SM2 digest"); + + let mut signature = [0u8; 80]; + let signature_len = key + .sign_hash(&digest, &mut signature, &rng) + .expect("Error signing SM2 digest"); + assert!(signature_len > 0 && signature_len <= signature.len()); + + let valid = key + .verify_hash(&signature[..signature_len], &digest) + .expect("Error verifying SM2 signature"); + assert!(valid); + + digest[0] ^= 0x01; + let valid = key + .verify_hash(&signature[..signature_len], &digest) + .expect("Error verifying modified SM2 digest"); + assert!(!valid); +} + +#[test] +#[cfg(all(random, sm2_digest))] +fn test_sm2_digest_rejects_small_buffer() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); + let mut digest = [0u8; 31]; + + let result = key.create_digest( + SM2::CERT_SIG_ID, + b"message digest", + SM2::HASH_TYPE_SM3, + &mut digest, + ); + assert!(result.is_err()); +} + +#[test] +#[cfg(all(random, sm2_sign))] +fn test_sm2_sign_rejects_small_buffer() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); + let digest = [0x42u8; 32]; + let mut signature = [0u8; 1]; + + assert!(key.sign_hash(&digest, &mut signature, &rng).is_err()); +} + +#[test] +#[cfg(all(random, sm2_dh))] +fn test_sm2_shared_secret() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + let mut alice = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Alice key"); + let mut bob = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Bob key"); + let mut alice_secret = [0u8; SM2::KEY_SIZE]; + let mut bob_secret = [0u8; SM2::KEY_SIZE]; + + let alice_len = alice + .shared_secret(&mut bob, &mut alice_secret) + .expect("Error deriving Alice shared secret"); + let bob_len = bob + .shared_secret(&mut alice, &mut bob_secret) + .expect("Error deriving Bob shared secret"); + + assert_eq!(alice_len, SM2::KEY_SIZE); + assert_eq!(alice_len, bob_len); + assert_eq!(alice_secret[..alice_len], bob_secret[..bob_len]); +} + +#[test] +#[cfg(all(random, sm2_dh))] +fn test_sm2_shared_secret_rejects_small_buffer() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + let mut alice = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Alice key"); + let mut bob = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Bob key"); + let mut secret = [0u8; 1]; + + assert!(alice.shared_secret(&mut bob, &mut secret).is_err()); +} From 2d03079d42f64b81f5f611ba209eedd4aa73609f Mon Sep 17 00:00:00 2001 From: somes <50438673+somes@users.noreply.github.com> Date: Fri, 19 Jun 2026 22:52:18 +0800 Subject: [PATCH 2/7] style(rust): remove redundant SM2 hash type cast --- wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs b/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs index 1adc7eac752..411c7c13c65 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs @@ -42,7 +42,7 @@ impl SM2 { pub const CERT_SIG_ID: &'static [u8] = b"1234567812345678"; /// wolfCrypt hash type identifier for SM3. - pub const HASH_TYPE_SM3: u32 = sys::wc_HashType_WC_HASH_TYPE_SM3 as u32; + pub const HASH_TYPE_SM3: u32 = sys::wc_HashType_WC_HASH_TYPE_SM3; /// No ECC operation flags. pub const FLAG_NONE: i32 = ECC::FLAG_NONE; From aca1547a1952284595e88f748ca0d465ca78c307 Mon Sep 17 00:00:00 2001 From: somes <50438673+somes@users.noreply.github.com> Date: Sat, 20 Jun 2026 01:53:24 +0800 Subject: [PATCH 3/7] build(rust): include SM2 files in distributions --- wrapper/rust/include.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wrapper/rust/include.am b/wrapper/rust/include.am index 4c3df1d122a..22656c1f64e 100644 --- a/wrapper/rust/include.am +++ b/wrapper/rust/include.am @@ -43,6 +43,7 @@ EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/rsa_pkcs1v15.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/scrypt_password_hash.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/sha.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/sha_digest.rs +EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/sys.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/common/mod.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_aes.rs @@ -75,4 +76,5 @@ EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_rsa_pkcs1v15.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_scrypt_password_hash.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_sha.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_sha_digest.rs +EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_wolfcrypt.rs From 9abbf8c6d628b47a853d2a626167b73c1c4fbd7e Mon Sep 17 00:00:00 2001 From: somes <50438673+somes@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:05:43 +0800 Subject: [PATCH 4/7] fix(rust): address SM2 review feedback --- wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md | 1 - wrapper/rust/wolfssl-wolfcrypt/build.rs | 3 + wrapper/rust/wolfssl-wolfcrypt/src/lib.rs | 1 - wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs | 89 +++++++++++++++++++ .../rust/wolfssl-wolfcrypt/tests/test_sm2.rs | 53 +++++++++-- 5 files changed, 136 insertions(+), 11 deletions(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md b/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md index 8a56c766a3d..620bbee02fd 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md +++ b/wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md @@ -11,7 +11,6 @@ New features: - Add BLAKE2 digest module (blake2_digest) - Add BLAKE2 MAC module (blake2_mac) - Add Aes192Ccm and Aes192Gcm -- Add SM2 wrapper (wolfssl_wolfcrypt::sm2 module) - Implement Clone for HMAC types - Improve cross-compilation and bare-metal target support in build.rs diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index 0ac5f2e0bef..2c735ad39da 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -504,6 +504,9 @@ fn scan_cfg() -> Result<()> { check_cfg(&binding, "wc_InitShake128", "shake128"); check_cfg(&binding, "wc_InitShake256", "shake256"); + /* sm3 */ + check_cfg(&binding, "wc_InitSm3", "sm3"); + /* sm2 */ check_cfg(&binding, "wc_ecc_sm2_make_key", "sm2"); check_cfg(&binding, "wc_ecc_sm2_shared_secret", "sm2_dh"); diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs b/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs index 1725e3fd6ed..169b35ad4a3 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs @@ -79,7 +79,6 @@ pub mod rsa_oaep; #[cfg(feature = "signature")] pub mod rsa_pkcs1v15; pub mod sha; -#[cfg(sm2)] pub mod sm2; #[cfg(all(feature = "password-hash", hmac, kdf_pbkdf2))] pub mod pbkdf2_password_hash; diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs b/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs index 411c7c13c65..beb6a5edb40 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs @@ -42,6 +42,7 @@ impl SM2 { pub const CERT_SIG_ID: &'static [u8] = b"1234567812345678"; /// wolfCrypt hash type identifier for SM3. + #[cfg(sm3)] pub const HASH_TYPE_SM3: u32 = sys::wc_HashType_WC_HASH_TYPE_SM3; /// No ECC operation flags. @@ -53,7 +54,52 @@ impl SM2 { /// Enable the ECC decrypt/sign flag. pub const FLAG_DEC_SIGN: i32 = ECC::FLAG_DEC_SIGN; + /// Bind an owned random number generator to this key for ECC operations + /// that require blinding. + /// + /// # Parameters + /// + /// * `rng`: The `RNG` struct instance to associate with this `SM2` + /// instance. + /// + /// # Returns + /// + /// Returns Ok(()) on success or Err(e) containing the wolfSSL library + /// error code value. + #[cfg(random)] + pub fn set_rng(&mut self, rng: RNG) -> Result<(), i32> { + self.key.set_rng(rng) + } + + /// Bind a shared random number generator to this key for ECC operations + /// that require blinding. Available when the `alloc` feature is enabled. + /// + /// # Parameters + /// + /// * `rng`: The reference-counted `RNG` struct instance to associate with + /// this `SM2` instance. + /// + /// # Returns + /// + /// Returns Ok(()) on success or Err(e) containing the wolfSSL library + /// error code value. + #[cfg(all(random, feature = "alloc"))] + pub fn set_shared_rng(&mut self, rng: alloc::rc::Rc) -> Result<(), i32> { + self.key.set_shared_rng(rng) + } + /// Generate a new SM2 key using the supplied random number generator. + /// + /// # Parameters + /// + /// * `rng`: Reference to an `RNG` struct to use for random number + /// generation while making the key. + /// * `flags`: Flags for making the key. + /// + /// # Returns + /// + /// Returns either Ok(SM2) containing the SM2 key or Err(e) containing + /// the wolfSSL library error code value. #[cfg(random)] pub fn generate(rng: &RNG, flags: i32) -> Result { let key = ECC::new()?; @@ -65,6 +111,16 @@ impl SM2 { } /// Derive a shared secret into the caller-supplied output buffer. + /// + /// # Parameters + /// + /// * `peer`: Peer `SM2` key containing the public component. + /// * `out`: Buffer in which to store the computed secret value. + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written to + /// `out` or Err(e) containing the wolfSSL library error code value. #[cfg(sm2_dh)] pub fn shared_secret(&mut self, peer: &mut SM2, out: &mut [u8]) -> Result { let mut out_len = crate::buffer_len_to_u32(out.len())?; @@ -83,6 +139,18 @@ impl SM2 { } /// Create an SM2 digest for an identity and message. + /// + /// # Parameters + /// + /// * `id`: Identity associated with the signer. + /// * `message`: Message to digest. + /// * `hash_type`: wolfCrypt hash type identifier. + /// * `out`: Buffer in which to store the digest. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. #[cfg(sm2_digest)] pub fn create_digest( &mut self, @@ -113,6 +181,17 @@ impl SM2 { } /// Sign a hash with this SM2 key and return the DER signature length. + /// + /// # Parameters + /// + /// * `hash`: Message digest to sign. + /// * `signature`: Buffer in which to store the DER-encoded signature. + /// * `rng`: Random number generator used while signing. + /// + /// # Returns + /// + /// Returns either Ok(size) containing the number of bytes written to + /// `signature` or Err(e) containing the wolfSSL library error code value. #[cfg(all(sm2_sign, random))] pub fn sign_hash( &mut self, @@ -139,6 +218,16 @@ impl SM2 { } /// Verify a DER-encoded SM2 signature against a hash. + /// + /// # Parameters + /// + /// * `signature`: DER-encoded SM2 signature to verify. + /// * `hash`: Message digest associated with the signature. + /// + /// # Returns + /// + /// Returns either Ok(true) for a valid signature, Ok(false) for an invalid + /// signature, or Err(e) containing the wolfSSL library error code value. #[cfg(sm2_verify)] pub fn verify_hash(&mut self, signature: &[u8], hash: &[u8]) -> Result { let signature_len = crate::buffer_len_to_u32(signature.len())?; diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs index e084ea61ab0..0b12f97bb00 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs @@ -2,10 +2,34 @@ mod common; +#[cfg(all(random, sm2_dh))] +use std::rc::Rc; #[cfg(random)] use wolfssl_wolfcrypt::random::RNG; use wolfssl_wolfcrypt::sm2::SM2; +#[test] +#[cfg(random)] +fn test_sm2_set_rng() { + common::setup(); + let key_gen_rng = RNG::new().expect("Failed to create key generation RNG"); + let blinding_rng = RNG::new().expect("Failed to create blinding RNG"); + let mut key = SM2::generate(&key_gen_rng, SM2::FLAG_NONE).expect("Error with generate()"); + + key.set_rng(blinding_rng).expect("Error with set_rng()"); +} + +#[test] +#[cfg(all(random, feature = "alloc"))] +fn test_sm2_set_shared_rng() { + common::setup(); + let rng = Rc::new(RNG::new().expect("Failed to create RNG")); + let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error with generate()"); + + key.set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); +} + #[test] #[cfg(random)] fn test_sm2_generate() { @@ -15,8 +39,8 @@ fn test_sm2_generate() { } #[test] -#[cfg(all(random, sm2_digest))] -fn test_sm2_create_digest() { +#[cfg(all(random, sm2_digest, sm3))] +fn test_sm2_create_digest_with_sm3() { common::setup(); let rng = RNG::new().expect("Failed to create RNG"); let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); @@ -34,8 +58,8 @@ fn test_sm2_create_digest() { } #[test] -#[cfg(all(random, sm2_digest, sm2_sign, sm2_verify))] -fn test_sm2_sign_and_verify() { +#[cfg(all(random, sm2_digest, sm2_sign, sm2_verify, sm3))] +fn test_sm2_sign_and_verify_with_sm3_digest() { common::setup(); let rng = RNG::new().expect("Failed to create RNG"); let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); @@ -67,8 +91,8 @@ fn test_sm2_sign_and_verify() { } #[test] -#[cfg(all(random, sm2_digest))] -fn test_sm2_digest_rejects_small_buffer() { +#[cfg(all(random, sm2_digest, sm3))] +fn test_sm2_create_digest_with_sm3_rejects_small_buffer() { common::setup(); let rng = RNG::new().expect("Failed to create RNG"); let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); @@ -99,9 +123,14 @@ fn test_sm2_sign_rejects_small_buffer() { #[cfg(all(random, sm2_dh))] fn test_sm2_shared_secret() { common::setup(); - let rng = RNG::new().expect("Failed to create RNG"); + let rng = Rc::new(RNG::new().expect("Failed to create RNG")); let mut alice = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Alice key"); let mut bob = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Bob key"); + alice + .set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); + bob.set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); let mut alice_secret = [0u8; SM2::KEY_SIZE]; let mut bob_secret = [0u8; SM2::KEY_SIZE]; @@ -112,7 +141,8 @@ fn test_sm2_shared_secret() { .shared_secret(&mut alice, &mut bob_secret) .expect("Error deriving Bob shared secret"); - assert_eq!(alice_len, SM2::KEY_SIZE); + assert!(alice_len > 0 && alice_len <= SM2::KEY_SIZE); + assert!(bob_len > 0 && bob_len <= SM2::KEY_SIZE); assert_eq!(alice_len, bob_len); assert_eq!(alice_secret[..alice_len], bob_secret[..bob_len]); } @@ -121,9 +151,14 @@ fn test_sm2_shared_secret() { #[cfg(all(random, sm2_dh))] fn test_sm2_shared_secret_rejects_small_buffer() { common::setup(); - let rng = RNG::new().expect("Failed to create RNG"); + let rng = Rc::new(RNG::new().expect("Failed to create RNG")); let mut alice = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Alice key"); let mut bob = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Bob key"); + alice + .set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); + bob.set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); let mut secret = [0u8; 1]; assert!(alice.shared_secret(&mut bob, &mut secret).is_err()); From e93b450d74aed0508353cd01359fe3b24cbf2547 Mon Sep 17 00:00:00 2001 From: somes <50438673+somes@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:40:52 +0800 Subject: [PATCH 5/7] test(rust): cover SM2 raw hash signatures --- .../rust/wolfssl-wolfcrypt/tests/test_sm2.rs | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs index 0b12f97bb00..af3c55e5546 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs @@ -19,17 +19,6 @@ fn test_sm2_set_rng() { key.set_rng(blinding_rng).expect("Error with set_rng()"); } -#[test] -#[cfg(all(random, feature = "alloc"))] -fn test_sm2_set_shared_rng() { - common::setup(); - let rng = Rc::new(RNG::new().expect("Failed to create RNG")); - let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error with generate()"); - - key.set_shared_rng(Rc::clone(&rng)) - .expect("Error with set_shared_rng()"); -} - #[test] #[cfg(random)] fn test_sm2_generate() { @@ -119,6 +108,32 @@ fn test_sm2_sign_rejects_small_buffer() { assert!(key.sign_hash(&digest, &mut signature, &rng).is_err()); } +#[test] +#[cfg(all(random, sm2_sign, sm2_verify))] +fn test_sm2_sign_and_verify_hash() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); + let mut digest = [0x42u8; 32]; + let mut signature = [0u8; 80]; + + let signature_len = key + .sign_hash(&digest, &mut signature, &rng) + .expect("Error signing SM2 hash"); + assert!(signature_len > 0 && signature_len <= signature.len()); + + let valid = key + .verify_hash(&signature[..signature_len], &digest) + .expect("Error verifying SM2 signature"); + assert!(valid); + + digest[0] ^= 0x01; + let valid = key + .verify_hash(&signature[..signature_len], &digest) + .expect("Error verifying modified SM2 hash"); + assert!(!valid); +} + #[test] #[cfg(all(random, sm2_dh))] fn test_sm2_shared_secret() { From 0de526b58a5ba0289fef85238b8c976141458118 Mon Sep 17 00:00:00 2001 From: somes <50438673+somes@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:54:56 +0800 Subject: [PATCH 6/7] test(rust): rename SM2 sign hash buffer test --- wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs index af3c55e5546..5efb13cc4d2 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs @@ -98,7 +98,7 @@ fn test_sm2_create_digest_with_sm3_rejects_small_buffer() { #[test] #[cfg(all(random, sm2_sign))] -fn test_sm2_sign_rejects_small_buffer() { +fn test_sm2_sign_hash_rejects_small_buffer() { common::setup(); let rng = RNG::new().expect("Failed to create RNG"); let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); From da950f07c957de50b9942304d2974ed14068663e Mon Sep 17 00:00:00 2001 From: somes <50438673+somes@users.noreply.github.com> Date: Wed, 24 Jun 2026 16:38:04 +0800 Subject: [PATCH 7/7] test(rust): order SM2 tests by operation --- .../rust/wolfssl-wolfcrypt/tests/test_sm2.rs | 152 +++++++++--------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs index 5efb13cc4d2..5192687bf40 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs @@ -27,6 +27,51 @@ fn test_sm2_generate() { SM2::generate(&rng, SM2::FLAG_NONE).expect("Error with generate()"); } +#[test] +#[cfg(all(random, sm2_dh))] +fn test_sm2_shared_secret() { + common::setup(); + let rng = Rc::new(RNG::new().expect("Failed to create RNG")); + let mut alice = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Alice key"); + let mut bob = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Bob key"); + alice + .set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); + bob.set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); + let mut alice_secret = [0u8; SM2::KEY_SIZE]; + let mut bob_secret = [0u8; SM2::KEY_SIZE]; + + let alice_len = alice + .shared_secret(&mut bob, &mut alice_secret) + .expect("Error deriving Alice shared secret"); + let bob_len = bob + .shared_secret(&mut alice, &mut bob_secret) + .expect("Error deriving Bob shared secret"); + + assert!(alice_len > 0 && alice_len <= SM2::KEY_SIZE); + assert!(bob_len > 0 && bob_len <= SM2::KEY_SIZE); + assert_eq!(alice_len, bob_len); + assert_eq!(alice_secret[..alice_len], bob_secret[..bob_len]); +} + +#[test] +#[cfg(all(random, sm2_dh))] +fn test_sm2_shared_secret_rejects_small_buffer() { + common::setup(); + let rng = Rc::new(RNG::new().expect("Failed to create RNG")); + let mut alice = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Alice key"); + let mut bob = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Bob key"); + alice + .set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); + bob.set_shared_rng(Rc::clone(&rng)) + .expect("Error with set_shared_rng()"); + let mut secret = [0u8; 1]; + + assert!(alice.shared_secret(&mut bob, &mut secret).is_err()); +} + #[test] #[cfg(all(random, sm2_digest, sm3))] fn test_sm2_create_digest_with_sm3() { @@ -47,24 +92,34 @@ fn test_sm2_create_digest_with_sm3() { } #[test] -#[cfg(all(random, sm2_digest, sm2_sign, sm2_verify, sm3))] -fn test_sm2_sign_and_verify_with_sm3_digest() { +#[cfg(all(random, sm2_digest, sm3))] +fn test_sm2_create_digest_with_sm3_rejects_small_buffer() { common::setup(); let rng = RNG::new().expect("Failed to create RNG"); let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); - let mut digest = [0u8; 32]; - key.create_digest( + let mut digest = [0u8; 31]; + + let result = key.create_digest( SM2::CERT_SIG_ID, b"message digest", SM2::HASH_TYPE_SM3, &mut digest, - ) - .expect("Error creating SM2 digest"); + ); + assert!(result.is_err()); +} +#[test] +#[cfg(all(random, sm2_sign, sm2_verify))] +fn test_sm2_sign_and_verify_hash() { + common::setup(); + let rng = RNG::new().expect("Failed to create RNG"); + let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); + let mut digest = [0x42u8; 32]; let mut signature = [0u8; 80]; + let signature_len = key .sign_hash(&digest, &mut signature, &rng) - .expect("Error signing SM2 digest"); + .expect("Error signing SM2 hash"); assert!(signature_len > 0 && signature_len <= signature.len()); let valid = key @@ -75,27 +130,10 @@ fn test_sm2_sign_and_verify_with_sm3_digest() { digest[0] ^= 0x01; let valid = key .verify_hash(&signature[..signature_len], &digest) - .expect("Error verifying modified SM2 digest"); + .expect("Error verifying modified SM2 hash"); assert!(!valid); } -#[test] -#[cfg(all(random, sm2_digest, sm3))] -fn test_sm2_create_digest_with_sm3_rejects_small_buffer() { - common::setup(); - let rng = RNG::new().expect("Failed to create RNG"); - let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); - let mut digest = [0u8; 31]; - - let result = key.create_digest( - SM2::CERT_SIG_ID, - b"message digest", - SM2::HASH_TYPE_SM3, - &mut digest, - ); - assert!(result.is_err()); -} - #[test] #[cfg(all(random, sm2_sign))] fn test_sm2_sign_hash_rejects_small_buffer() { @@ -109,17 +147,24 @@ fn test_sm2_sign_hash_rejects_small_buffer() { } #[test] -#[cfg(all(random, sm2_sign, sm2_verify))] -fn test_sm2_sign_and_verify_hash() { +#[cfg(all(random, sm2_digest, sm2_sign, sm2_verify, sm3))] +fn test_sm2_sign_and_verify_with_sm3_digest() { common::setup(); let rng = RNG::new().expect("Failed to create RNG"); let mut key = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating SM2 key"); - let mut digest = [0x42u8; 32]; - let mut signature = [0u8; 80]; + let mut digest = [0u8; 32]; + key.create_digest( + SM2::CERT_SIG_ID, + b"message digest", + SM2::HASH_TYPE_SM3, + &mut digest, + ) + .expect("Error creating SM2 digest"); + let mut signature = [0u8; 80]; let signature_len = key .sign_hash(&digest, &mut signature, &rng) - .expect("Error signing SM2 hash"); + .expect("Error signing SM2 digest"); assert!(signature_len > 0 && signature_len <= signature.len()); let valid = key @@ -130,51 +175,6 @@ fn test_sm2_sign_and_verify_hash() { digest[0] ^= 0x01; let valid = key .verify_hash(&signature[..signature_len], &digest) - .expect("Error verifying modified SM2 hash"); + .expect("Error verifying modified SM2 digest"); assert!(!valid); } - -#[test] -#[cfg(all(random, sm2_dh))] -fn test_sm2_shared_secret() { - common::setup(); - let rng = Rc::new(RNG::new().expect("Failed to create RNG")); - let mut alice = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Alice key"); - let mut bob = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Bob key"); - alice - .set_shared_rng(Rc::clone(&rng)) - .expect("Error with set_shared_rng()"); - bob.set_shared_rng(Rc::clone(&rng)) - .expect("Error with set_shared_rng()"); - let mut alice_secret = [0u8; SM2::KEY_SIZE]; - let mut bob_secret = [0u8; SM2::KEY_SIZE]; - - let alice_len = alice - .shared_secret(&mut bob, &mut alice_secret) - .expect("Error deriving Alice shared secret"); - let bob_len = bob - .shared_secret(&mut alice, &mut bob_secret) - .expect("Error deriving Bob shared secret"); - - assert!(alice_len > 0 && alice_len <= SM2::KEY_SIZE); - assert!(bob_len > 0 && bob_len <= SM2::KEY_SIZE); - assert_eq!(alice_len, bob_len); - assert_eq!(alice_secret[..alice_len], bob_secret[..bob_len]); -} - -#[test] -#[cfg(all(random, sm2_dh))] -fn test_sm2_shared_secret_rejects_small_buffer() { - common::setup(); - let rng = Rc::new(RNG::new().expect("Failed to create RNG")); - let mut alice = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Alice key"); - let mut bob = SM2::generate(&rng, SM2::FLAG_NONE).expect("Error generating Bob key"); - alice - .set_shared_rng(Rc::clone(&rng)) - .expect("Error with set_shared_rng()"); - bob.set_shared_rng(Rc::clone(&rng)) - .expect("Error with set_shared_rng()"); - let mut secret = [0u8; 1]; - - assert!(alice.shared_secret(&mut bob, &mut secret).is_err()); -}