Skip to content

Commit cc77cbb

Browse files
Rust wrapper: implement kem traits
1 parent 2e77948 commit cc77cbb

6 files changed

Lines changed: 436 additions & 1 deletion

File tree

wrapper/rust/wolfssl-wolfcrypt/Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wrapper/rust/wolfssl-wolfcrypt/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ cipher = ["dep:cipher"]
1818
digest = ["dep:digest"]
1919
signature = ["dep:signature"]
2020
password-hash = ["dep:password-hash", "password-hash/phc"]
21+
kem = ["dep:kem", "hybrid-array/extra-sizes"]
2122

2223
[dependencies]
2324
rand_core = { version = "0.10", optional = true, default-features = false }
@@ -27,13 +28,16 @@ digest = { version = "0.11", optional = true, default-features = false, features
2728
signature = { version = "2.2", optional = true, default-features = false }
2829
zeroize = { version = "1.3", default-features = false, features = ["derive"] }
2930
password-hash = { version = "0.6.1", optional = true, default-features = false }
31+
kem = { version = "0.3", optional = true, default-features = false }
32+
hybrid-array = { version = "0.4.7", optional = true, default-features = false }
3033

3134
[dev-dependencies]
3235
aead = { version = "0.5", features = ["alloc", "dev"] }
3336
cipher = "0.5"
3437
digest = { version = "0.11", features = ["dev"] }
3538
signature = "2.2"
3639
password-hash = { version = "0.6.1", features = ["phc"] }
40+
kem = "0.3"
3741

3842
[build-dependencies]
3943
bindgen = "0.72.1"

wrapper/rust/wolfssl-wolfcrypt/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FEATURES := rand_core,aead,cipher,digest,signature,password-hash
1+
FEATURES := rand_core,aead,cipher,digest,signature,password-hash,kem
22
CARGO_FEATURE_FLAGS := --features $(FEATURES)
33

44
.PHONY: all

wrapper/rust/wolfssl-wolfcrypt/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ pub mod hmac;
5858
pub mod kdf;
5959
pub mod lms;
6060
pub mod mlkem;
61+
#[cfg(feature = "kem")]
62+
pub mod mlkem_kem;
6163
pub mod prf;
6264
pub mod random;
6365
pub mod rsa;
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/*
2+
* Copyright (C) 2006-2026 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+
RustCrypto `kem` trait implementations for the wolfCrypt ML-KEM types.
23+
24+
Provides [`kem::Kem`] marker types and associated encapsulation/decapsulation
25+
key types for ML-KEM-512, ML-KEM-768, and ML-KEM-1024:
26+
27+
| Marker | Encapsulation key | Decapsulation key |
28+
|-----------------|---------------------------------|---------------------------------|
29+
| [`MlKem512`] | [`MlKem512EncapsulationKey`] | [`MlKem512DecapsulationKey`] |
30+
| [`MlKem768`] | [`MlKem768EncapsulationKey`] | [`MlKem768DecapsulationKey`] |
31+
| [`MlKem1024`] | [`MlKem1024EncapsulationKey`] | [`MlKem1024DecapsulationKey`] |
32+
33+
Each encapsulation key implements [`kem::Encapsulate`] (with
34+
[`kem::TryKeyInit`] and [`kem::KeyExport`] for key serialization).
35+
36+
Each decapsulation key implements [`kem::Decapsulate`] and
37+
[`kem::Generate`] (for key generation from a [`rand_core::CryptoRng`]).
38+
39+
Key generation and encapsulation bridge a caller-supplied
40+
[`rand_core::CryptoRng`] to wolfCrypt's deterministic APIs by extracting the
41+
required random bytes from the RNG.
42+
43+
# Examples
44+
45+
```rust
46+
#[cfg(all(mlkem, random, feature = "kem", feature = "rand_core"))]
47+
{
48+
use kem::{Kem, Encapsulate, Decapsulate};
49+
use kem::Generate;
50+
use wolfssl_wolfcrypt::random::RNG;
51+
use wolfssl_wolfcrypt::mlkem_kem::*;
52+
53+
let mut rng = RNG::new().expect("RNG creation failed");
54+
55+
let (dk, ek) = MlKem768::generate_keypair_from_rng(&mut rng);
56+
let (ct, k_send) = ek.encapsulate_with_rng(&mut rng);
57+
let k_recv = dk.decapsulate(&ct);
58+
assert_eq!(k_send, k_recv);
59+
}
60+
```
61+
*/
62+
63+
#![cfg(all(feature = "kem", mlkem))]
64+
65+
use kem::common::array::Array;
66+
use kem::common::typenum::{U32, U768, U800};
67+
use hybrid_array::sizes::{U1088, U1184, U1568, U1632, U2400, U3168};
68+
69+
macro_rules! impl_mlkem_kem {
70+
(
71+
kem = $kem:ident,
72+
ek = $ek:ident,
73+
dk = $dk:ident,
74+
pk_typenum = $pk_tn:ty,
75+
sk_typenum = $sk_tn:ty,
76+
ct_typenum = $ct_tn:ty,
77+
pk_len = $pk_len:expr,
78+
sk_len = $sk_len:expr,
79+
ct_len = $ct_len:expr,
80+
key_type = $key_type:expr $(,)?
81+
) => {
82+
/// ML-KEM parameter set marker implementing [`kem::Kem`].
83+
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
84+
pub struct $kem;
85+
86+
impl kem::Kem for $kem {
87+
type DecapsulationKey = $dk;
88+
type EncapsulationKey = $ek;
89+
type SharedKeySize = U32;
90+
type CiphertextSize = $ct_tn;
91+
}
92+
93+
/// ML-KEM encapsulation (public) key implementing [`kem::Encapsulate`].
94+
#[derive(Clone, Debug, PartialEq, Eq)]
95+
pub struct $ek {
96+
pk: Array<u8, $pk_tn>,
97+
}
98+
99+
impl kem::KeySizeUser for $ek {
100+
type KeySize = $pk_tn;
101+
}
102+
103+
impl kem::TryKeyInit for $ek {
104+
fn new(key: &kem::Key<Self>) -> Result<Self, kem::InvalidKey> {
105+
let mut wc_key = crate::mlkem::MlKem::new($key_type)
106+
.map_err(|_| kem::InvalidKey)?;
107+
wc_key.decode_public_key(key.as_ref())
108+
.map_err(|_| kem::InvalidKey)?;
109+
Ok(Self { pk: key.clone() })
110+
}
111+
}
112+
113+
impl kem::KeyExport for $ek {
114+
fn to_bytes(&self) -> kem::Key<Self> {
115+
self.pk.clone()
116+
}
117+
}
118+
119+
impl kem::Encapsulate for $ek {
120+
type Kem = $kem;
121+
122+
fn encapsulate_with_rng<R: kem::common::rand_core::CryptoRng + ?Sized>(
123+
&self,
124+
rng: &mut R,
125+
) -> (kem::Ciphertext<$kem>, kem::SharedKey<$kem>) {
126+
let mut rand = [0u8; crate::mlkem::MlKem::ENC_RAND_SIZE];
127+
rng.fill_bytes(&mut rand);
128+
129+
let mut wc_key = crate::mlkem::MlKem::new($key_type)
130+
.expect("MlKem::new failed");
131+
wc_key.decode_public_key(self.pk.as_ref())
132+
.expect("decode_public_key failed");
133+
134+
let mut ct = [0u8; $ct_len];
135+
let mut ss = [0u8; crate::mlkem::MlKem::SHARED_SECRET_SIZE];
136+
wc_key.encapsulate_with_random(&mut ct, &mut ss, &rand)
137+
.expect("encapsulate_with_random failed");
138+
139+
(ct.into(), ss.into())
140+
}
141+
}
142+
143+
/// ML-KEM decapsulation (private) key implementing [`kem::Decapsulate`].
144+
///
145+
/// The private key bytes are securely zeroized on drop.
146+
pub struct $dk {
147+
sk: Array<u8, $sk_tn>,
148+
ek: $ek,
149+
}
150+
151+
impl kem::Decapsulator for $dk {
152+
type Kem = $kem;
153+
154+
fn encapsulation_key(&self) -> &$ek {
155+
&self.ek
156+
}
157+
}
158+
159+
impl kem::Decapsulate for $dk {
160+
fn decapsulate(
161+
&self,
162+
ct: &kem::Ciphertext<$kem>,
163+
) -> kem::SharedKey<$kem> {
164+
let mut wc_key = crate::mlkem::MlKem::new($key_type)
165+
.expect("MlKem::new failed");
166+
wc_key.decode_private_key(self.sk.as_ref())
167+
.expect("decode_private_key failed");
168+
169+
let mut ss = [0u8; crate::mlkem::MlKem::SHARED_SECRET_SIZE];
170+
wc_key.decapsulate(&mut ss, ct.as_ref())
171+
.expect("decapsulate failed");
172+
173+
ss.into()
174+
}
175+
}
176+
177+
impl kem::Generate for $dk {
178+
fn try_generate_from_rng<R: kem::common::rand_core::TryCryptoRng + ?Sized>(
179+
rng: &mut R,
180+
) -> Result<Self, R::Error> {
181+
let mut rand = [0u8; crate::mlkem::MlKem::MAKEKEY_RAND_SIZE];
182+
rng.try_fill_bytes(&mut rand)?;
183+
184+
let wc_key = crate::mlkem::MlKem::generate_with_random(
185+
$key_type, &rand,
186+
).expect("generate_with_random failed");
187+
188+
let mut pk = [0u8; $pk_len];
189+
let mut sk = [0u8; $sk_len];
190+
wc_key.encode_public_key(&mut pk)
191+
.expect("encode_public_key failed");
192+
wc_key.encode_private_key(&mut sk)
193+
.expect("encode_private_key failed");
194+
195+
Ok(Self {
196+
sk: sk.into(),
197+
ek: $ek { pk: pk.into() },
198+
})
199+
}
200+
}
201+
202+
impl Drop for $dk {
203+
fn drop(&mut self) {
204+
use zeroize::Zeroize;
205+
let sk_bytes: &mut [u8] = self.sk.as_mut();
206+
sk_bytes.zeroize();
207+
}
208+
}
209+
};
210+
}
211+
212+
impl_mlkem_kem! {
213+
kem = MlKem512,
214+
ek = MlKem512EncapsulationKey,
215+
dk = MlKem512DecapsulationKey,
216+
pk_typenum = U800,
217+
sk_typenum = U1632,
218+
ct_typenum = U768,
219+
pk_len = 800,
220+
sk_len = 1632,
221+
ct_len = 768,
222+
key_type = crate::mlkem::MlKem::TYPE_512,
223+
}
224+
225+
impl_mlkem_kem! {
226+
kem = MlKem768,
227+
ek = MlKem768EncapsulationKey,
228+
dk = MlKem768DecapsulationKey,
229+
pk_typenum = U1184,
230+
sk_typenum = U2400,
231+
ct_typenum = U1088,
232+
pk_len = 1184,
233+
sk_len = 2400,
234+
ct_len = 1088,
235+
key_type = crate::mlkem::MlKem::TYPE_768,
236+
}
237+
238+
impl_mlkem_kem! {
239+
kem = MlKem1024,
240+
ek = MlKem1024EncapsulationKey,
241+
dk = MlKem1024DecapsulationKey,
242+
pk_typenum = U1568,
243+
sk_typenum = U3168,
244+
ct_typenum = U1568,
245+
pk_len = 1568,
246+
sk_len = 3168,
247+
ct_len = 1568,
248+
key_type = crate::mlkem::MlKem::TYPE_1024,
249+
}

0 commit comments

Comments
 (0)