Skip to content

Commit a28e107

Browse files
Merge pull request wolfSSL#9336 from holtrop/rust-wc-kdf-prf
Rust wrapper: add wolfssl::wolfcrypt::kdf, wolfssl::wolfcrypt::prf
2 parents 62deeed + 61a277c commit a28e107

File tree

15 files changed

+451
-40
lines changed

15 files changed

+451
-40
lines changed

doc/dox_comments/header_files/hmac.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,8 +534,7 @@ int wc_Tls13_HKDF_Expand_Label_ex(
534534
\ingroup HMAC
535535
536536
\brief Expand data using HMAC, salt and label and info. TLS v1.3 defines
537-
this function for key derivation. This is the _ex version adding heap hint
538-
and device identifier.
537+
this function for key derivation.
539538
540539
\return 0 Returned upon successfully generating a key with the given inputs
541540
\return BAD_FUNC_ARG Returned if an invalid hash type is given (see type param)

wrapper/rust/include.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/dh.rs
2121
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/ecc.rs
2222
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/hkdf.rs
2323
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/hmac.rs
24+
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/kdf.rs
25+
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/prf.rs
2426
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/random.rs
2527
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/rsa.rs
2628
EXTRA_DIST += wrapper/rust/wolfssl/src/wolfcrypt/sha.rs
@@ -29,6 +31,8 @@ EXTRA_DIST += wrapper/rust/wolfssl/tests/test_dh.rs
2931
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_ecc.rs
3032
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_hkdf.rs
3133
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_hmac.rs
34+
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_kdf.rs
35+
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_prf.rs
3236
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_random.rs
3337
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_rsa.rs
3438
EXTRA_DIST += wrapper/rust/wolfssl/tests/test_sha.rs

wrapper/rust/wolfssl/src/wolfcrypt.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub mod dh;
2323
pub mod ecc;
2424
pub mod hkdf;
2525
pub mod hmac;
26+
pub mod kdf;
27+
pub mod prf;
2628
pub mod random;
2729
pub mod rsa;
2830
pub mod sha;

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ the raw C functions in a memory-safe and easy-to-use Rust API.
2727
*/
2828

2929
use std::mem::{size_of, MaybeUninit};
30-
use std::ptr::{null, null_mut};
3130
use wolfssl_sys as ws;
3231

3332
/// AES Cipher Block Chaining (CBC) mode.
@@ -989,7 +988,7 @@ impl ECB {
989988
let key_size = key.len() as u32;
990989
let rc = unsafe {
991990
ws::wc_AesSetKey(&mut self.ws_aes, key_ptr, key_size,
992-
null(), dir)
991+
core::ptr::null(), dir)
993992
};
994993
if rc != 0 {
995994
return Err(rc);
@@ -2357,7 +2356,7 @@ impl Drop for XTSStream {
23572356
fn new_ws_aes() -> Result<ws::Aes, i32> {
23582357
let mut ws_aes: MaybeUninit<ws::Aes> = MaybeUninit::uninit();
23592358
let rc = unsafe {
2360-
ws::wc_AesInit(ws_aes.as_mut_ptr(), null_mut(), ws::INVALID_DEVID)
2359+
ws::wc_AesInit(ws_aes.as_mut_ptr(), core::ptr::null_mut(), ws::INVALID_DEVID)
23612360
};
23622361
if rc != 0 {
23632362
return Err(rc);
@@ -2369,7 +2368,7 @@ fn new_ws_aes() -> Result<ws::Aes, i32> {
23692368
fn new_ws_xtsaes() -> Result<ws::XtsAes, i32> {
23702369
let mut ws_xtsaes: MaybeUninit<ws::XtsAes> = MaybeUninit::uninit();
23712370
let rc = unsafe {
2372-
ws::wc_AesXtsInit(ws_xtsaes.as_mut_ptr(), null_mut(), ws::INVALID_DEVID)
2371+
ws::wc_AesXtsInit(ws_xtsaes.as_mut_ptr(), core::ptr::null_mut(), ws::INVALID_DEVID)
23732372
};
23742373
if rc != 0 {
23752374
return Err(rc);

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ wolfSSL `DhKey` object. It ensures proper initialization and deallocation.
3232
use wolfssl_sys as ws;
3333

3434
use std::mem::{MaybeUninit};
35-
use std::ptr::null;
3635
use crate::wolfcrypt::random::RNG;
3736

3837
pub struct DH {
@@ -138,7 +137,7 @@ impl DH {
138137
let p_size = p.len() as u32;
139138
let g_size = g.len() as u32;
140139
let mut no_q = 1i32;
141-
let mut q_ptr: *const u8 = null();
140+
let mut q_ptr: *const u8 = core::ptr::null();
142141
let mut q_size = 0u32;
143142
if let Some(q) = q {
144143
no_q = 0;
@@ -791,7 +790,7 @@ impl DH {
791790
/// ```
792791
pub fn check_priv_key_ex(&mut self, private: &[u8], prime: Option<&[u8]>) -> Result<(), i32> {
793792
let private_size = private.len() as u32;
794-
let mut prime_ptr: *const u8 = null();
793+
let mut prime_ptr: *const u8 = core::ptr::null();
795794
let mut prime_size = 0u32;
796795
if let Some(prime) = prime {
797796
prime_ptr = prime.as_ptr();

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ wolfSSL `ecc_key` object. It ensures proper initialization and deallocation.
3232
use wolfssl_sys as ws;
3333

3434
use std::mem::{MaybeUninit};
35-
use std::ptr::null_mut;
3635
use crate::wolfcrypt::random::RNG;
3736

3837
/// Rust wrapper for wolfSSL `ecc_point` object.
@@ -1339,10 +1338,10 @@ impl ECC {
13391338
pub fn make_pub(&mut self, rng: Option<&mut RNG>) -> Result<(), i32> {
13401339
let rng_ptr = match rng {
13411340
Some(rng) => &mut rng.wc_rng,
1342-
None => null_mut(),
1341+
None => core::ptr::null_mut(),
13431342
};
13441343
let rc = unsafe {
1345-
ws::wc_ecc_make_pub_ex(&mut self.wc_ecc_key, null_mut(), rng_ptr)
1344+
ws::wc_ecc_make_pub_ex(&mut self.wc_ecc_key, core::ptr::null_mut(), rng_ptr)
13461345
};
13471346
if rc != 0 {
13481347
return Err(rc);
@@ -1377,7 +1376,7 @@ impl ECC {
13771376
pub fn make_pub_to_point(&mut self, rng: Option<&mut RNG>) -> Result<ECCPoint, i32> {
13781377
let rng_ptr = match rng {
13791378
Some(rng) => &mut rng.wc_rng,
1380-
None => null_mut(),
1379+
None => core::ptr::null_mut(),
13811380
};
13821381
let wc_ecc_point = unsafe { ws::wc_ecc_new_point() };
13831382
if wc_ecc_point.is_null() {

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ use wolfssl_sys as ws;
5252
/// ```rust
5353
/// use wolfssl::wolfcrypt::hkdf::*;
5454
/// use wolfssl::wolfcrypt::hmac::HMAC;
55-
/// use wolfssl_sys as ws;
55+
/// use wolfssl::wolfcrypt::sha::SHA256;
5656
/// let ikm = b"MyPassword0";
5757
/// let salt = b"12345678ABCDEFGH";
58-
/// let mut extract_out = [0u8; ws::WC_SHA256_DIGEST_SIZE as usize];
58+
/// let mut extract_out = [0u8; SHA256::DIGEST_SIZE];
5959
/// hkdf_extract(HMAC::TYPE_SHA256, Some(salt), ikm, &mut extract_out).expect("Error with hkdf_extract()");
6060
/// ```
6161
pub fn hkdf_extract(typ: i32, salt: Option<&[u8]>, key: &[u8], out: &mut [u8]) -> Result<(), i32> {
@@ -102,10 +102,10 @@ pub fn hkdf_extract(typ: i32, salt: Option<&[u8]>, key: &[u8], out: &mut [u8]) -
102102
/// ```rust
103103
/// use wolfssl::wolfcrypt::hkdf::*;
104104
/// use wolfssl::wolfcrypt::hmac::HMAC;
105-
/// use wolfssl_sys as ws;
105+
/// use wolfssl::wolfcrypt::sha::SHA256;
106106
/// let ikm = b"MyPassword0";
107107
/// let salt = b"12345678ABCDEFGH";
108-
/// let mut extract_out = [0u8; ws::WC_SHA256_DIGEST_SIZE as usize];
108+
/// let mut extract_out = [0u8; SHA256::DIGEST_SIZE];
109109
/// hkdf_extract(HMAC::TYPE_SHA256, Some(salt), ikm, &mut extract_out).expect("Error with hkdf_extract()");
110110
/// let info = b"0";
111111
/// let mut expand_out = [0u8; 16];
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
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 Key Derivation
23+
Function (KDF) 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 crate::wolfcrypt::hmac::HMAC;
30+
use wolfssl_sys as ws;
31+
32+
/// Perform RFC 5869 HKDF-Extract operation for TLS v1.3 key derivation.
33+
///
34+
/// # Parameters
35+
///
36+
/// * `typ`: Hash type, one of `HMAC::TYPE_*`.
37+
/// * `salt`: Optional Salt value.
38+
/// * `key`: Optional Initial Key Material (IKM).
39+
/// * `out`: Output buffer to store TLS1.3 HKDF-Extract result (generated
40+
/// Pseudo-Random Key (PRK)). The size of this buffer must match
41+
/// `HMAC::get_hmac_size_by_type(typ)`.
42+
///
43+
/// # Returns
44+
///
45+
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
46+
/// library error code value.
47+
///
48+
/// # Example
49+
///
50+
/// ```rust
51+
/// use wolfssl::wolfcrypt::hmac::HMAC;
52+
/// use wolfssl::wolfcrypt::kdf::*;
53+
/// use wolfssl::wolfcrypt::sha::SHA256;
54+
/// let mut secret = [0u8; SHA256::DIGEST_SIZE];
55+
/// tls13_hkdf_extract(HMAC::TYPE_SHA256, None, None, &mut secret).expect("Error with tls13_hkdf_extract()");
56+
/// ```
57+
pub fn tls13_hkdf_extract(typ: i32, salt: Option<&[u8]>, key: Option<&mut [u8]>, out: &mut [u8]) -> Result<(), i32> {
58+
let mut salt_ptr = core::ptr::null();
59+
let mut salt_size = 0u32;
60+
if let Some(salt) = salt {
61+
salt_ptr = salt.as_ptr();
62+
salt_size = salt.len() as u32;
63+
}
64+
let mut ikm_buf = [0u8; ws::WC_MAX_DIGEST_SIZE as usize];
65+
let mut ikm_ptr = ikm_buf.as_mut_ptr();
66+
let mut ikm_size = 0u32;
67+
if let Some(key) = key {
68+
if key.len() > 0 {
69+
ikm_ptr = key.as_mut_ptr();
70+
ikm_size = key.len() as u32;
71+
}
72+
}
73+
if out.len() != HMAC::get_hmac_size_by_type(typ)? {
74+
return Err(ws::wolfCrypt_ErrorCodes_BUFFER_E);
75+
}
76+
let rc = unsafe {
77+
ws::wc_Tls13_HKDF_Extract(out.as_mut_ptr(), salt_ptr, salt_size,
78+
ikm_ptr, ikm_size, typ)
79+
};
80+
if rc != 0 {
81+
return Err(rc);
82+
}
83+
Ok(())
84+
}
85+
86+
/// Perform RFC 5869 HKDF-Extract operation for TLS v1.3 key derivation.
87+
///
88+
/// This utilizes HMAC to convert `key`, `label`, and `info` into a
89+
/// derived key which is written to `out`.
90+
///
91+
/// # Parameters
92+
///
93+
/// * `typ`: Hash type, one of `HMAC::TYPE_*`.
94+
/// * `key`: Key to use for KDF (typically output of `tls13_hkdf_extract()`).
95+
/// * `protocol`: Buffer containing TLS protocol.
96+
/// * `label`: Buffer containing label.
97+
/// * `info`: Buffer containing additional info.
98+
/// * `out`: Output buffer to store TLS1.3 HKDF-Expand result. The buffer can be
99+
/// any size.
100+
///
101+
/// # Returns
102+
///
103+
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
104+
/// library error code value.
105+
///
106+
/// # Example
107+
///
108+
/// ```rust
109+
/// use wolfssl::wolfcrypt::hmac::HMAC;
110+
/// use wolfssl::wolfcrypt::kdf::*;
111+
/// use wolfssl::wolfcrypt::sha::SHA256;
112+
/// let hash_hello1 = [
113+
/// 0x63u8, 0x83, 0x58, 0xab, 0x36, 0xcd, 0x0c, 0xf3,
114+
/// 0x26, 0x07, 0xb5, 0x5f, 0x0b, 0x8b, 0x45, 0xd6,
115+
/// 0x7d, 0x5b, 0x42, 0xdc, 0xa8, 0xaa, 0x06, 0xfb,
116+
/// 0x20, 0xa5, 0xbb, 0x85, 0xdb, 0x54, 0xd8, 0x8b
117+
/// ];
118+
/// let client_early_traffic_secret = [
119+
/// 0x20u8, 0x18, 0x72, 0x7c, 0xde, 0x3a, 0x85, 0x17, 0x72, 0xdc, 0xd7, 0x72,
120+
/// 0xb0, 0xfc, 0x45, 0xd0, 0x62, 0xb9, 0xbb, 0x38, 0x69, 0x05, 0x7b, 0xb4,
121+
/// 0x5e, 0x58, 0x5d, 0xed, 0xcd, 0x0b, 0x96, 0xd3
122+
/// ];
123+
/// let mut secret = [0u8; SHA256::DIGEST_SIZE];
124+
/// tls13_hkdf_extract(HMAC::TYPE_SHA256, None, None, &mut secret).expect("Error with tls13_hkdf_extract()");
125+
/// let protocol_label = b"tls13 ";
126+
/// let ce_traffic_label = b"c e traffic";
127+
/// let mut expand_out = [0u8; SHA256::DIGEST_SIZE];
128+
/// tls13_hkdf_expand_label(HMAC::TYPE_SHA256, &secret,
129+
/// protocol_label, ce_traffic_label,
130+
/// &hash_hello1, &mut expand_out).expect("Error with tls13_hkdf_expand_label()");
131+
/// ```
132+
pub fn tls13_hkdf_expand_label(typ: i32, key: &[u8], protocol: &[u8], label: &[u8], info: &[u8], out: &mut [u8]) -> Result<(), i32> {
133+
let key_size = key.len() as u32;
134+
let protocol_size = protocol.len() as u32;
135+
let label_size = label.len() as u32;
136+
let info_size = info.len() as u32;
137+
let out_size = out.len() as u32;
138+
let rc = unsafe {
139+
ws::wc_Tls13_HKDF_Expand_Label(out.as_mut_ptr(), out_size,
140+
key.as_ptr(), key_size, protocol.as_ptr(), protocol_size,
141+
label.as_ptr(), label_size, info.as_ptr(), info_size, typ)
142+
};
143+
if rc != 0 {
144+
return Err(rc);
145+
}
146+
Ok(())
147+
}
148+
149+
/// Perform SSH KDF operation.
150+
///
151+
/// # Parameters
152+
///
153+
/// * `typ`: Hash type, one of `HMAC::TYPE_*`.
154+
/// * `key_id`: Key ID, typically 'A' through 'F'.
155+
/// * `k`: Initial key.
156+
/// * `h`: Exchange hash.
157+
/// * `session_id`: Unique identifier for the SSH session.
158+
/// * `key`: Output buffer.
159+
///
160+
/// # Returns
161+
///
162+
/// Returns either Ok(()) on success or Err(e) containing the wolfSSL
163+
/// library error code value.
164+
///
165+
/// # Example
166+
///
167+
/// ```rust
168+
/// use wolfssl::wolfcrypt::hmac::HMAC;
169+
/// use wolfssl::wolfcrypt::kdf::*;
170+
/// let k = [0x42u8; 256];
171+
/// let h = [0x43u8; 32];
172+
/// let sid = [0x44u8; 32];
173+
/// let mut out = [0u8; 16];
174+
/// ssh_kdf(HMAC::TYPE_SHA256, b'A', &k, &h, &sid, &mut out).expect("Error with ssh_kdf()");
175+
/// ```
176+
pub fn ssh_kdf(typ: i32, key_id: u8, k: &[u8], h: &[u8], session_id: &[u8], key: &mut [u8]) -> Result<(), i32> {
177+
let key_size = key.len() as u32;
178+
let k_size = k.len() as u32;
179+
let h_size = h.len() as u32;
180+
let session_size = session_id.len() as u32;
181+
let rc = unsafe {
182+
ws::wc_SSH_KDF(typ as u8, key_id,
183+
key.as_mut_ptr(), key_size,
184+
k.as_ptr(), k_size, h.as_ptr(), h_size,
185+
session_id.as_ptr(), session_size)
186+
};
187+
if rc != 0 {
188+
return Err(rc);
189+
}
190+
Ok(())
191+
}

0 commit comments

Comments
 (0)