diff --git a/wrapper/rust/include.am b/wrapper/rust/include.am index 5b5338e3115..2e97bac6082 100644 --- a/wrapper/rust/include.am +++ b/wrapper/rust/include.am @@ -4,6 +4,7 @@ EXTRA_DIST += wrapper/rust/Makefile EXTRA_DIST += wrapper/rust/README.md +EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/Cargo.lock EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/Cargo.toml EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/Makefile @@ -11,6 +12,7 @@ EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/README.md EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/build.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/headers.h EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/aes.rs +EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/blake2.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/cmac.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/dh.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs @@ -26,6 +28,7 @@ EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/rsa.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/sha.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/src/sys.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_aes.rs +EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_blake2.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_cmac.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_dh.rs EXTRA_DIST += wrapper/rust/wolfssl-wolfcrypt/tests/test_ecc.rs diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index a60acf1827e..bd37c199018 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -126,6 +126,10 @@ fn scan_cfg() -> Result<()> { check_cfg(&binding, "wc_AesXtsInit", "aes_xts"); check_cfg(&binding, "wc_AesXtsEncryptInit", "aes_xts_stream"); + /* blake2 */ + check_cfg(&binding, "wc_InitBlake2b", "blake2b"); + check_cfg(&binding, "wc_InitBlake2s", "blake2s"); + /* cmac */ check_cfg(&binding, "wc_InitCmac", "cmac"); diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/blake2.rs b/wrapper/rust/wolfssl-wolfcrypt/src/blake2.rs new file mode 100644 index 00000000000..3023adefcd7 --- /dev/null +++ b/wrapper/rust/wolfssl-wolfcrypt/src/blake2.rs @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2025 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 the wolfCrypt library's BLAKE2 +functionality. +*/ + +use crate::sys; +use std::mem::MaybeUninit; + +/// Context for BLAKE2b computation. +#[cfg(blake2b)] +pub struct BLAKE2b { + wc_blake2b: sys::Blake2b, +} + +#[cfg(blake2b)] +impl BLAKE2b { + /// Build a new BLAKE2b instance. + /// + /// # Parameters + /// + /// * `digest_size`: Length of the blake 2 digest to implement. + /// + /// # Returns + /// + /// Returns either Ok(blake2b) containing the BLAKE2b struct instance or + /// Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// use wolfssl_wolfcrypt::blake2::BLAKE2b; + /// let blake2b = BLAKE2b::new(64).expect("Error with new()"); + /// ``` + pub fn new(digest_size: usize) -> Result { + let digest_size = digest_size as u32; + let mut wc_blake2b: MaybeUninit = MaybeUninit::uninit(); + let rc = unsafe { + sys::wc_InitBlake2b(wc_blake2b.as_mut_ptr(), digest_size) + }; + if rc != 0 { + return Err(rc); + } + let wc_blake2b = unsafe { wc_blake2b.assume_init() }; + let blake2b = BLAKE2b { wc_blake2b }; + Ok(blake2b) + } + + /// Build a new BLAKE2b instance. + /// + /// # Parameters + /// + /// * `digest_size`: Length of the blake 2 digest to implement. + /// * `key`: Key to use for BLAKE2b operation. + /// + /// # Returns + /// + /// Returns either Ok(blake2b) containing the BLAKE2b struct instance or + /// Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// use wolfssl_wolfcrypt::blake2::BLAKE2b; + /// let key = [42u8; 32]; + /// let blake2b = BLAKE2b::new_with_key(64, &key).expect("Error with new()"); + /// ``` + pub fn new_with_key(digest_size: usize, key: &[u8]) -> Result { + let digest_size = digest_size as u32; + let mut wc_blake2b: MaybeUninit = MaybeUninit::uninit(); + let key_size = key.len() as u32; + let rc = unsafe { + sys::wc_InitBlake2b_WithKey(wc_blake2b.as_mut_ptr(), digest_size, + key.as_ptr(), key_size) + }; + if rc != 0 { + return Err(rc); + } + let wc_blake2b = unsafe { wc_blake2b.assume_init() }; + let blake2b = BLAKE2b { wc_blake2b }; + Ok(blake2b) + } + + /// Update the BLAKE2b hash with the input data. + /// + /// This method may be called several times and then the finalize() + /// method should be called to retrieve the final hash. + /// + /// # Parameters + /// + /// * `data`: Input data to hash. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// use wolfssl_wolfcrypt::blake2::BLAKE2b; + /// let mut blake2b = BLAKE2b::new(64).expect("Error with new()"); + /// blake2b.update(&[0u8; 16]).expect("Error with update()"); + /// ``` + pub fn update(&mut self, data: &[u8]) -> Result<(), i32> { + let data_size = data.len() as u32; + let rc = unsafe { + sys::wc_Blake2bUpdate(&mut self.wc_blake2b, data.as_ptr(), data_size) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Compute and retrieve the final BLAKE2b hash value. + /// + /// # Parameters + /// + /// * `hash`: Output buffer in which to store the computed BLAKE2b hash + /// value. It can be any length. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// use wolfssl_wolfcrypt::blake2::BLAKE2b; + /// let mut blake2b = BLAKE2b::new(64).expect("Error with new()"); + /// blake2b.update(&[0u8; 16]).expect("Error with update()"); + /// let mut hash = [0u8; 64]; + /// blake2b.finalize(&mut hash).expect("Error with finalize()"); + /// ``` + pub fn finalize(&mut self, hash: &mut [u8]) -> Result<(), i32> { + let hash_size = hash.len() as u32; + let rc = unsafe { + sys::wc_Blake2bFinal(&mut self.wc_blake2b, hash.as_mut_ptr(), hash_size) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } +} + +/// Context for BLAKE2s computation. +#[cfg(blake2s)] +pub struct BLAKE2s { + wc_blake2s: sys::Blake2s, +} + +#[cfg(blake2s)] +impl BLAKE2s { + /// Build a new BLAKE2s instance. + /// + /// # Parameters + /// + /// * `digest_size`: Length of the blake 2 digest to implement. + /// + /// # Returns + /// + /// Returns either Ok(blake2s) containing the BLAKE2s struct instance or + /// Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// use wolfssl_wolfcrypt::blake2::BLAKE2s; + /// let blake2s = BLAKE2s::new(32).expect("Error with new()"); + /// ``` + pub fn new(digest_size: usize) -> Result { + let digest_size = digest_size as u32; + let mut wc_blake2s: MaybeUninit = MaybeUninit::uninit(); + let rc = unsafe { + sys::wc_InitBlake2s(wc_blake2s.as_mut_ptr(), digest_size) + }; + if rc != 0 { + return Err(rc); + } + let wc_blake2s = unsafe { wc_blake2s.assume_init() }; + let blake2s = BLAKE2s { wc_blake2s }; + Ok(blake2s) + } + + /// Build a new BLAKE2s instance. + /// + /// # Parameters + /// + /// * `digest_size`: Length of the blake 2 digest to implement. + /// * `key`: Key to use for BLAKE2s operation. + /// + /// # Returns + /// + /// Returns either Ok(blake2s) containing the BLAKE2s struct instance or + /// Err(e) containing the wolfSSL library error code value. + /// + /// # Example + /// + /// ```rust + /// use wolfssl_wolfcrypt::blake2::BLAKE2s; + /// let key = [42u8; 32]; + /// let blake2s = BLAKE2s::new_with_key(32, &key).expect("Error with new()"); + /// ``` + pub fn new_with_key(digest_size: usize, key: &[u8]) -> Result { + let digest_size = digest_size as u32; + let mut wc_blake2s: MaybeUninit = MaybeUninit::uninit(); + let key_size = key.len() as u32; + let rc = unsafe { + sys::wc_InitBlake2s_WithKey(wc_blake2s.as_mut_ptr(), digest_size, + key.as_ptr(), key_size) + }; + if rc != 0 { + return Err(rc); + } + let wc_blake2s = unsafe { wc_blake2s.assume_init() }; + let blake2s = BLAKE2s { wc_blake2s }; + Ok(blake2s) + } + + /// Update the BLAKE2s hash with the input data. + /// + /// This method may be called several times and then the finalize() + /// method should be called to retrieve the final hash. + /// + /// # Parameters + /// + /// * `data`: Input data to hash. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// use wolfssl_wolfcrypt::blake2::BLAKE2s; + /// let mut blake2s = BLAKE2s::new(32).expect("Error with new()"); + /// blake2s.update(&[0u8; 16]).expect("Error with update()"); + /// ``` + pub fn update(&mut self, data: &[u8]) -> Result<(), i32> { + let data_size = data.len() as u32; + let rc = unsafe { + sys::wc_Blake2sUpdate(&mut self.wc_blake2s, data.as_ptr(), data_size) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } + + /// Compute and retrieve the final BLAKE2s hash value. + /// + /// # Parameters + /// + /// * `hash`: Output buffer in which to store the computed BLAKE2s hash + /// value. It can be any length. + /// + /// # Returns + /// + /// Returns either Ok(()) on success or Err(e) containing the wolfSSL + /// library error code value. + /// + /// # Example + /// + /// ```rust + /// use wolfssl_wolfcrypt::blake2::BLAKE2s; + /// let mut blake2s = BLAKE2s::new(32).expect("Error with new()"); + /// blake2s.update(&[0u8; 16]).expect("Error with update()"); + /// let mut hash = [0u8; 64]; + /// blake2s.finalize(&mut hash).expect("Error with finalize()"); + /// ``` + pub fn finalize(&mut self, hash: &mut [u8]) -> Result<(), i32> { + let hash_size = hash.len() as u32; + let rc = unsafe { + sys::wc_Blake2sFinal(&mut self.wc_blake2s, hash.as_mut_ptr(), hash_size) + }; + if rc != 0 { + return Err(rc); + } + Ok(()) + } +} diff --git a/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs b/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs index d7c96fd6660..02523f57325 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/src/lib.rs @@ -22,6 +22,7 @@ pub mod sys; pub mod aes; +pub mod blake2; pub mod cmac; pub mod dh; pub mod ecc; diff --git a/wrapper/rust/wolfssl-wolfcrypt/tests/test_blake2.rs b/wrapper/rust/wolfssl-wolfcrypt/tests/test_blake2.rs new file mode 100644 index 00000000000..5891ea5968e --- /dev/null +++ b/wrapper/rust/wolfssl-wolfcrypt/tests/test_blake2.rs @@ -0,0 +1,88 @@ +#[cfg(any(blake2b, blake2s))] +use wolfssl_wolfcrypt::blake2::*; + +#[test] +#[cfg(blake2b)] +fn test_blake2b() { + let expected_hashes: [&[u8]; 3] = [ + &[ + 0x78, 0x6A, 0x02, 0xF7, 0x42, 0x01, 0x59, 0x03, + 0xC6, 0xC6, 0xFD, 0x85, 0x25, 0x52, 0xD2, 0x72, + 0x91, 0x2F, 0x47, 0x40, 0xE1, 0x58, 0x47, 0x61, + 0x8A, 0x86, 0xE2, 0x17, 0xF7, 0x1F, 0x54, 0x19, + 0xD2, 0x5E, 0x10, 0x31, 0xAF, 0xEE, 0x58, 0x53, + 0x13, 0x89, 0x64, 0x44, 0x93, 0x4E, 0xB0, 0x4B, + 0x90, 0x3A, 0x68, 0x5B, 0x14, 0x48, 0xB7, 0x55, + 0xD5, 0x6F, 0x70, 0x1A, 0xFE, 0x9B, 0xE2, 0xCE + ], + &[ + 0x2F, 0xA3, 0xF6, 0x86, 0xDF, 0x87, 0x69, 0x95, + 0x16, 0x7E, 0x7C, 0x2E, 0x5D, 0x74, 0xC4, 0xC7, + 0xB6, 0xE4, 0x8F, 0x80, 0x68, 0xFE, 0x0E, 0x44, + 0x20, 0x83, 0x44, 0xD4, 0x80, 0xF7, 0x90, 0x4C, + 0x36, 0x96, 0x3E, 0x44, 0x11, 0x5F, 0xE3, 0xEB, + 0x2A, 0x3A, 0xC8, 0x69, 0x4C, 0x28, 0xBC, 0xB4, + 0xF5, 0xA0, 0xF3, 0x27, 0x6F, 0x2E, 0x79, 0x48, + 0x7D, 0x82, 0x19, 0x05, 0x7A, 0x50, 0x6E, 0x4B + ], + &[ + 0x1C, 0x08, 0x79, 0x8D, 0xC6, 0x41, 0xAB, 0xA9, + 0xDE, 0xE4, 0x35, 0xE2, 0x25, 0x19, 0xA4, 0x72, + 0x9A, 0x09, 0xB2, 0xBF, 0xE0, 0xFF, 0x00, 0xEF, + 0x2D, 0xCD, 0x8E, 0xD6, 0xF8, 0xA0, 0x7D, 0x15, + 0xEA, 0xF4, 0xAE, 0xE5, 0x2B, 0xBF, 0x18, 0xAB, + 0x56, 0x08, 0xA6, 0x19, 0x0F, 0x70, 0xB9, 0x04, + 0x86, 0xC8, 0xA7, 0xD4, 0x87, 0x37, 0x10, 0xB1, + 0x11, 0x5D, 0x3D, 0xEB, 0xBB, 0x43, 0x27, 0xB5 + ], + ]; + + for (i, expected_hash) in expected_hashes.iter().enumerate() { + let mut blake2b = BLAKE2b::new(expected_hash.len()).expect("Error with new()"); + let mut input = vec![0u8; i]; + for idx in 0..input.len() { + input[idx] = idx as u8; + } + blake2b.update(&input).expect("Error with update()"); + let mut hash = [0u8; 64]; + blake2b.finalize(&mut hash).expect("error with finalize()"); + assert_eq!(hash, *expected_hash); + } +} + +#[test] +#[cfg(blake2s)] +fn test_blake2s() { + let expected_hashes: [&[u8]; 3] = [ + &[ + 0x69, 0x21, 0x7a, 0x30, 0x79, 0x90, 0x80, 0x94, + 0xe1, 0x11, 0x21, 0xd0, 0x42, 0x35, 0x4a, 0x7c, + 0x1f, 0x55, 0xb6, 0x48, 0x2c, 0xa1, 0xa5, 0x1e, + 0x1b, 0x25, 0x0d, 0xfd, 0x1e, 0xd0, 0xee, 0xf9, + ], + &[ + 0xe3, 0x4d, 0x74, 0xdb, 0xaf, 0x4f, 0xf4, 0xc6, + 0xab, 0xd8, 0x71, 0xcc, 0x22, 0x04, 0x51, 0xd2, + 0xea, 0x26, 0x48, 0x84, 0x6c, 0x77, 0x57, 0xfb, + 0xaa, 0xc8, 0x2f, 0xe5, 0x1a, 0xd6, 0x4b, 0xea, + ], + &[ + 0xdd, 0xad, 0x9a, 0xb1, 0x5d, 0xac, 0x45, 0x49, + 0xba, 0x42, 0xf4, 0x9d, 0x26, 0x24, 0x96, 0xbe, + 0xf6, 0xc0, 0xba, 0xe1, 0xdd, 0x34, 0x2a, 0x88, + 0x08, 0xf8, 0xea, 0x26, 0x7c, 0x6e, 0x21, 0x0c, + ], + ]; + + for (i, expected_hash) in expected_hashes.iter().enumerate() { + let mut blake2s = BLAKE2s::new(expected_hash.len()).expect("Error with new()"); + let mut input = vec![0u8; i]; + for idx in 0..input.len() { + input[idx] = idx as u8; + } + blake2s.update(&input).expect("Error with update()"); + let mut hash = [0u8; 32]; + blake2s.finalize(&mut hash).expect("error with finalize()"); + assert_eq!(hash, *expected_hash); + } +}