Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions wrapper/rust/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions wrapper/rust/wolfssl-wolfcrypt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
1 change: 1 addition & 0 deletions wrapper/rust/wolfssl-wolfcrypt/headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
21 changes: 21 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, i32> {
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<i32>,
) -> Result<Self, i32> {
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) };
Expand Down
2 changes: 2 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down
162 changes: 162 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/src/sm2.rs
Original file line number Diff line number Diff line change
@@ -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;

/// 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<Self, i32> {
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<usize, i32> {
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<usize, i32> {
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<bool, i32> {
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)
}
}
130 changes: 130 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/tests/test_sm2.rs
Original file line number Diff line number Diff line change
@@ -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());
}
Loading