Skip to content

Commit 1d64a4e

Browse files
authored
Merge pull request #9352 from holtrop/rust-wc-cmac
Rust wrapper: add wolfssl::wolfcrypt::cmac module
2 parents a4be322 + ef92114 commit 1d64a4e

File tree

6 files changed

+286
-3
lines changed

6 files changed

+286
-3
lines changed

doc/dox_comments/header_files/cmac.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ int wc_AesCmacGenerate(byte* out, word32* outSz,
174174
\ingroup CMAC
175175
\brief Single shot function for validating a CMAC
176176
\return 0 on success
177-
\param check pointer to return the result
178-
\param checkSz size of checkout buffer
177+
\param check CMAC value to verify
178+
\param checkSz size of check buffer
179179
\param in input data to process
180180
\param inSz size of input data
181181
\param key key pointer

wrapper/rust/include.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ EXTRA_DIST += wrapper/rust/wolfssl/build.rs
1717
EXTRA_DIST += wrapper/rust/wolfssl/src/lib.rs
1818
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt.rs
1919
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/aes.rs
20+
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/cmac.rs
2021
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/dh.rs
2122
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/ecc.rs
2223
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/hkdf.rs
@@ -27,6 +28,7 @@ EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/random.rs
2728
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/rsa.rs
2829
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/sha.rs
2930
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_aes.rs
31+
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_cmac.rs
3032
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_dh.rs
3133
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_ecc.rs
3234
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_hkdf.rs

wrapper/rust/wolfssl/src/wolfcrypt.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020

2121
pub mod aes;
22+
pub mod cmac;
2223
pub mod dh;
2324
pub mod ecc;
2425
pub mod hkdf;
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
/*
2+
* Copyright (C) 2025 wolfSSL Inc.
3+
*
4+
* This file is part of wolfSSL.
5+
*
6+
* wolfSSL is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* wolfSSL is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
19+
*/
20+
21+
/*!
22+
This module provides a Rust wrapper for the wolfCrypt library's Cipher-based
23+
Message Authentication Code (CMAC) functionality.
24+
25+
It leverages the `wolfssl-sys` crate for low-level FFI bindings, encapsulating
26+
the raw C functions in a memory-safe and easy-to-use Rust API.
27+
*/
28+
29+
use std::mem::MaybeUninit;
30+
use wolfssl_sys as ws;
31+
32+
/// The `CMAC` struct manages the lifecycle of a wolfSSL `Cmac` object.
33+
///
34+
/// It ensures proper initialization and deallocation.
35+
///
36+
/// An instance can be created with `new()`.
37+
pub struct CMAC {
38+
ws_cmac: ws::Cmac,
39+
}
40+
impl CMAC {
41+
/// One-shot CMAC generation function.
42+
///
43+
/// # Parameters
44+
///
45+
/// * `key`: Key to use for CMAC generation.
46+
/// * `data`: CMAC input data.
47+
/// * `dout`: Output buffer where CMAC is written.
48+
///
49+
/// # Returns
50+
///
51+
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
52+
/// library error code value.
53+
///
54+
/// # Example
55+
///
56+
/// ```rust
57+
/// use wolfssl::wolfcrypt::cmac::CMAC;
58+
/// let key = [
59+
/// 0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
60+
/// 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
61+
/// ];
62+
/// let message = [
63+
/// 0x6bu8, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
64+
/// 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
65+
/// ];
66+
/// let mut generate_out = [0u8; 16];
67+
/// CMAC::generate(&key, &message, &mut generate_out).expect("Error with generate()");
68+
/// ```
69+
pub fn generate(key: &[u8], data: &[u8], dout: &mut [u8]) -> Result<(), i32> {
70+
let key_size = key.len() as u32;
71+
let data_size = data.len() as u32;
72+
let mut dout_size = dout.len() as u32;
73+
let rc = unsafe {
74+
ws::wc_AesCmacGenerate(dout.as_mut_ptr(), &mut dout_size,
75+
data.as_ptr(), data_size,
76+
key.as_ptr(), key_size)
77+
};
78+
if rc != 0 {
79+
return Err(rc);
80+
}
81+
Ok(())
82+
}
83+
84+
/// Create a new CMAC object using the given key.
85+
///
86+
/// # Parameters
87+
///
88+
/// * `key`: Key to use for CMAC generation.
89+
///
90+
/// # Returns
91+
///
92+
/// Returns either Ok(cmac) containing the CMAC struct instance or Err(e)
93+
/// containing the wolfSSL library error code value.
94+
///
95+
/// # Example
96+
///
97+
/// ```rust
98+
/// use wolfssl::wolfcrypt::cmac::CMAC;
99+
/// let key = [
100+
/// 0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
101+
/// 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
102+
/// ];
103+
/// let mut cmac = CMAC::new(&key).expect("Error with new()");
104+
/// ```
105+
pub fn new(key: &[u8]) -> Result<Self, i32> {
106+
let key_size = key.len() as u32;
107+
let mut ws_cmac: MaybeUninit<ws::Cmac> = MaybeUninit::uninit();
108+
let typ = ws::CmacType_WC_CMAC_AES as i32;
109+
let rc = unsafe {
110+
ws::wc_InitCmac(ws_cmac.as_mut_ptr(), key.as_ptr(), key_size,
111+
typ, core::ptr::null_mut())
112+
};
113+
if rc != 0 {
114+
return Err(rc);
115+
}
116+
let ws_cmac = unsafe { ws_cmac.assume_init() };
117+
let cmac = CMAC { ws_cmac };
118+
Ok(cmac)
119+
}
120+
121+
/// One-shot CMAC verification function.
122+
///
123+
/// # Parameters
124+
///
125+
/// * `key`: Key to use for CMAC generation.
126+
/// * `data`: CMAC input data.
127+
/// * `check`: CMAC value to compare to.
128+
///
129+
/// # Returns
130+
///
131+
/// Returns either Ok(valid) (with valid indicating if the CMAC passed in
132+
/// is correct or not) on success or Err(e) containing the wolfSSL library
133+
/// error code value.
134+
///
135+
/// # Example
136+
///
137+
/// ```rust
138+
/// use wolfssl::wolfcrypt::cmac::CMAC;
139+
/// let key = [
140+
/// 0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
141+
/// 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
142+
/// ];
143+
/// let message = [
144+
/// 0x6bu8, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
145+
/// 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
146+
/// ];
147+
/// let mut generate_out = [0u8; 16];
148+
/// CMAC::generate(&key, &message, &mut generate_out).expect("Error with generate()");
149+
/// let valid = CMAC::verify(&key, &message, &generate_out).expect("Error with verify()");
150+
/// assert!(valid);
151+
/// ```
152+
pub fn verify(key: &[u8], data: &[u8], check: &[u8]) -> Result<bool, i32> {
153+
let key_size = key.len() as u32;
154+
let data_size = data.len() as u32;
155+
let check_size = check.len() as u32;
156+
let rc = unsafe {
157+
ws::wc_AesCmacVerify(check.as_ptr(), check_size,
158+
data.as_ptr(), data_size,
159+
key.as_ptr(), key_size)
160+
};
161+
if rc < 0 {
162+
return Err(rc);
163+
}
164+
Ok(rc == 0)
165+
}
166+
167+
/// Add CMAC input data.
168+
///
169+
/// # Parameters
170+
///
171+
/// * `data`: CMAC input data.
172+
///
173+
/// # Returns
174+
///
175+
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
176+
/// library error code value.
177+
///
178+
/// # Example
179+
///
180+
/// ```rust
181+
/// use wolfssl::wolfcrypt::cmac::CMAC;
182+
/// let key = [
183+
/// 0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
184+
/// 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
185+
/// ];
186+
/// let message = [
187+
/// 0x6bu8, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
188+
/// 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
189+
/// ];
190+
/// let mut cmac = CMAC::new(&key).expect("Error with new()");
191+
/// cmac.update(&message).expect("Error with update()");
192+
/// ```
193+
pub fn update(&mut self, data: &[u8]) -> Result<(), i32> {
194+
let data_size = data.len() as u32;
195+
let rc = unsafe {
196+
ws::wc_CmacUpdate(&mut self.ws_cmac, data.as_ptr(), data_size)
197+
};
198+
if rc != 0 {
199+
return Err(rc);
200+
}
201+
Ok(())
202+
}
203+
204+
/// Generate the final Cipher-based Message Authentication Code result.
205+
///
206+
/// This function consumes the `CMAC` object since no further operations
207+
/// can be performed with it.
208+
///
209+
/// # Parameters
210+
///
211+
/// * `dout`: Output buffer where CMAC is written.
212+
///
213+
/// # Returns
214+
///
215+
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
216+
/// library error code value.
217+
///
218+
/// # Example
219+
///
220+
/// ```rust
221+
/// use wolfssl::wolfcrypt::cmac::CMAC;
222+
/// let key = [
223+
/// 0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
224+
/// 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
225+
/// ];
226+
/// let message = [
227+
/// 0x6bu8, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
228+
/// 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
229+
/// ];
230+
/// let mut cmac = CMAC::new(&key).expect("Error with new()");
231+
/// cmac.update(&message).expect("Error with update()");
232+
/// let mut finalize_out = [0u8; 16];
233+
/// cmac.finalize(&mut finalize_out).expect("Error with finalize()");
234+
/// ```
235+
pub fn finalize(mut self, dout: &mut [u8]) -> Result<(), i32> {
236+
let mut dout_size = dout.len() as u32;
237+
let rc = unsafe {
238+
ws::wc_CmacFinalNoFree(&mut self.ws_cmac,
239+
dout.as_mut_ptr(), &mut dout_size)
240+
};
241+
if rc != 0 {
242+
return Err(rc);
243+
}
244+
Ok(())
245+
}
246+
}
247+
impl Drop for CMAC {
248+
/// Safely free the wolfSSL resources.
249+
fn drop(&mut self) {
250+
unsafe { ws::wc_CmacFree(&mut self.ws_cmac); }
251+
}
252+
}

wrapper/rust/wolfssl/src/wolfcrypt/kdf.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn tls13_hkdf_extract(typ: i32, salt: Option<&[u8]>, key: Option<&mut [u8]>,
8383
Ok(())
8484
}
8585

86-
/// Perform RFC 5869 HKDF-Extract operation for TLS v1.3 key derivation.
86+
/// Perform RFC 5869 HKDF-Expand operation for TLS v1.3 key derivation.
8787
///
8888
/// This utilizes HMAC to convert `key`, `label`, and `info` into a
8989
/// derived key which is written to `out`.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use wolfssl::wolfcrypt::cmac::CMAC;
2+
3+
#[test]
4+
fn test_cmac() {
5+
let key = [
6+
0x2bu8, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
7+
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
8+
];
9+
let message = [
10+
0x6bu8, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
11+
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
12+
];
13+
let expected_cmac = [
14+
0x07u8, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
15+
0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c
16+
];
17+
let mut cmac = CMAC::new(&key).expect("Error with new()");
18+
cmac.update(&message).expect("Error with update()");
19+
let mut finalize_out = [0u8; 16];
20+
cmac.finalize(&mut finalize_out).expect("Error with finalize()");
21+
assert_eq!(finalize_out, expected_cmac);
22+
23+
let mut generate_out = [0u8; 16];
24+
CMAC::generate(&key, &message, &mut generate_out).expect("Error with generate()");
25+
assert_eq!(generate_out, finalize_out);
26+
let valid = CMAC::verify(&key, &message, &generate_out).expect("Error with verify()");
27+
assert!(valid);
28+
}

0 commit comments

Comments
 (0)