Skip to content

Commit bc588b1

Browse files
authored
Upgrade ml-kem from 0.2 to 0.3.0-rc.0 (#421)
1 parent b20d78c commit bc588b1

6 files changed

Lines changed: 72 additions & 42 deletions

File tree

.github/workflows/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- --features serde
2727
toolchain:
2828
- stable
29-
- 1.85.0
29+
- 1.87.0
3030
name: test
3131
env:
3232
PKCS11_MODULE: /usr/lib/softhsm/libsofthsm2.so
@@ -95,7 +95,7 @@ jobs:
9595
matrix:
9696
toolchain:
9797
- stable
98-
- 1.85.0
98+
- 1.87.0
9999
name: test simple_login command-line example
100100
steps:
101101
- name: install expect
@@ -118,7 +118,7 @@ jobs:
118118
matrix:
119119
toolchain:
120120
- stable
121-
- 1.85.0
121+
- 1.87.0
122122
name: test digital_locker command-line example
123123
steps:
124124
- name: install expect

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 4.1.0-pre.2 (March 26, 2026)
4+
* Upgraded ml-kem from 0.2 to 0.3.0-rc.0
5+
* Increased MSRV to 1.87
6+
37
## 4.1.0-pre.1 (November 17, 2025)
48
* Added ml-kem re-export behind the kem feature
59

Cargo.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ license = "Apache-2.0 OR MIT"
99
name = "opaque-ke"
1010
readme = "README.md"
1111
repository = "https://github.com/facebook/opaque-ke"
12-
rust-version = "1.85"
13-
version = "4.1.0-pre.1"
12+
rust-version = "1.87"
13+
version = "4.1.0-pre.2"
1414

1515
[features]
1616
argon2 = ["dep:argon2"]
1717
curve25519 = ["dep:curve25519-dalek"]
1818
default = ["ristretto255", "serde"]
1919
ecdsa = ["dep:ecdsa", "dep:rfc6979"]
2020
ed25519 = ["dep:curve25519-dalek", "dep:ed25519-dalek"]
21-
kem = ["dep:ml-kem"]
21+
kem = ["dep:ml-kem", "dep:rand_core_10"]
2222
ristretto255 = ["dep:curve25519-dalek", "voprf/ristretto255-ciphersuite"]
2323
serde = [
2424
"dep:serde",
@@ -54,10 +54,11 @@ elliptic-curve = { version = "0.13", features = ["hash2curve", "sec1"] }
5454
generic-array = "=0.14.7" # pinned to avoid deprecation warnings
5555
hkdf = "0.12"
5656
hmac = "0.12"
57-
ml-kem = { version = "0.2", default-features = false, features = [
57+
ml-kem = { version = "0.3.0-rc.0", default-features = false, features = [
5858
"zeroize",
5959
], optional = true }
6060
rand = { version = "0.8", default-features = false }
61+
rand_core_10 = { package = "rand_core", version = "0.10", default-features = false, optional = true }
6162
rfc6979 = { version = "0.4", optional = true }
6263
serde = { version = "1", default-features = false, features = [
6364
"derive",
@@ -73,7 +74,7 @@ getrandom = { version = "0.2", features = ["js"], optional = true }
7374
anyhow = "1"
7475
bincode = "1"
7576
chacha20poly1305 = "0.10"
76-
criterion = "0.7"
77+
criterion = "0.8"
7778
cryptoki = "0.9"
7879
elliptic-curve = { version = "0.13", features = ["alloc", "pkcs8"] }
7980
hex = "0.4"

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ Installation
2222
Add the following line to the dependencies of your `Cargo.toml`:
2323

2424
```
25-
opaque-ke = "4.1.0-pre.1"
25+
opaque-ke = "4.1.0-pre.2"
2626
```
2727

2828
### Minimum Supported Rust Version
2929

30-
Rust **1.85** or higher.
30+
Rust **1.87** or higher.
3131

3232
Audit
3333
-----

src/key_exchange/tripledh_kem.rs

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
//! the generic `ml-kem` abstractions into the existing OPAQUE key-exchange
1919
//! pipeline.
2020
21-
use core::convert::TryFrom;
2221
use core::fmt::Debug;
2322
use core::marker::PhantomData;
2423
use core::ops::Add;
@@ -29,9 +28,11 @@ use digest::{Digest, Output};
2928
use generic_array::sequence::Concat;
3029
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
3130
use generic_array::{ArrayLength, GenericArray};
32-
use ml_kem::kem::{Decapsulate, Encapsulate};
33-
use ml_kem::{
34-
Ciphertext as MlKemCiphertext, Encoded, EncodedSizeUser, KemCore, SharedKey as MlKemSharedKey,
31+
#[allow(deprecated)]
32+
use ml_kem::ExpandedKeyEncoding;
33+
use ml_kem::kem::{
34+
Ciphertext as MlKemCiphertext, Decapsulate, Encapsulate, Kem as MlKemTrait, KeyExport,
35+
KeySizeUser, TryKeyInit,
3536
};
3637
use rand::{CryptoRng, RngCore};
3738
use subtle::{ConstantTimeEq, CtOption};
@@ -44,7 +45,7 @@ use super::{
4445
SerializedIdentifiers,
4546
};
4647
use crate::ciphersuite::{CipherSuite, KeGroup};
47-
use crate::errors::{InternalError, ProtocolError};
48+
use crate::errors::ProtocolError;
4849
use crate::hash::{Hash, OutputSize, ProxyHash};
4950
use crate::key_exchange::group::Group;
5051
use crate::keypair::{PrivateKey, PublicKey};
@@ -115,16 +116,42 @@ pub trait KemCoreWrapper {
115116
) -> Result<GenericArray<u8, Self::SharedSecretLen>, ProtocolError>;
116117
}
117118

118-
type RcEncapsulationKeyLen<K> = <<K as KemCore>::EncapsulationKey as EncodedSizeUser>::EncodedSize;
119-
type RcDecapsulationKeyLen<K> = <<K as KemCore>::DecapsulationKey as EncodedSizeUser>::EncodedSize;
120-
type RcCiphertextLen<K> = <K as KemCore>::CiphertextSize;
121-
type RcSharedSecretLen<K> = <K as KemCore>::SharedKeySize;
119+
/// Adapter to bridge `rand 0.8` (`rand_core 0.6`) RNGs to `rand_core 0.10`
120+
/// which is required by `ml-kem 0.3.x`.
121+
struct RngCompat<'a, R>(&'a mut R);
122122

123+
impl<R: RngCore> rand_core_10::TryRng for RngCompat<'_, R> {
124+
type Error = core::convert::Infallible;
125+
126+
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
127+
Ok(self.0.next_u32())
128+
}
129+
130+
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
131+
Ok(self.0.next_u64())
132+
}
133+
134+
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
135+
self.0.fill_bytes(dst);
136+
Ok(())
137+
}
138+
}
139+
140+
impl<R: RngCore + CryptoRng> rand_core_10::TryCryptoRng for RngCompat<'_, R> {}
141+
142+
type RcEncapsulationKeyLen<K> = <<K as MlKemTrait>::EncapsulationKey as KeySizeUser>::KeySize;
143+
#[allow(deprecated)]
144+
type RcDecapsulationKeyLen<K> =
145+
<<K as MlKemTrait>::DecapsulationKey as ExpandedKeyEncoding>::EncodedSize;
146+
type RcCiphertextLen<K> = <K as MlKemTrait>::CiphertextSize;
147+
type RcSharedSecretLen<K> = <K as MlKemTrait>::SharedKeySize;
148+
149+
#[allow(deprecated)]
123150
impl<K> KemCoreWrapper for K
124151
where
125-
K: KemCore,
126-
K::EncapsulationKey: Encapsulate<MlKemCiphertext<K>, MlKemSharedKey<K>> + Clone,
127-
K::DecapsulationKey: Decapsulate<MlKemCiphertext<K>, MlKemSharedKey<K>> + Clone + ZeroizeOnDrop,
152+
K: MlKemTrait,
153+
K::EncapsulationKey: Encapsulate<Kem = K> + KeyExport + TryKeyInit + Clone,
154+
K::DecapsulationKey: Decapsulate<Kem = K> + ExpandedKeyEncoding + Clone + ZeroizeOnDrop,
128155
RcEncapsulationKeyLen<K>: ArrayLength<u8>,
129156
RcDecapsulationKeyLen<K>: ArrayLength<u8>,
130157
RcCiphertextLen<K>: ArrayLength<u8>,
@@ -140,39 +167,40 @@ where
140167
fn generate<R: RngCore + CryptoRng>(
141168
rng: &mut R,
142169
) -> Result<(Self::DecapsulationKey, Self::EncapsulationKey), ProtocolError> {
143-
Ok(K::generate(rng))
170+
Ok(K::generate_keypair_from_rng(&mut RngCompat(rng)))
144171
}
145172

146173
fn serialize_encapsulation_key(
147174
key: &Self::EncapsulationKey,
148175
) -> GenericArray<u8, Self::EncapsulationKeyLen> {
149-
GenericArray::clone_from_slice(key.as_bytes().as_slice())
176+
GenericArray::clone_from_slice(key.to_bytes().as_slice())
150177
}
151178

152179
fn deserialize_encapsulation_key(
153180
input: &mut &[u8],
154181
) -> Result<Self::EncapsulationKey, ProtocolError> {
155182
let bytes: GenericArray<u8, RcEncapsulationKeyLen<K>> =
156183
input.take_array("kem encapsulation key")?;
157-
let encoded = Encoded::<K::EncapsulationKey>::try_from(bytes.as_slice())
184+
let key = ml_kem::array::Array::try_from(bytes.as_slice())
158185
.map_err(|_| ProtocolError::SerializationError)?;
159-
Ok(K::EncapsulationKey::from_bytes(&encoded))
186+
TryKeyInit::new(&key).map_err(|_| ProtocolError::SerializationError)
160187
}
161188

162189
fn serialize_decapsulation_key(
163190
key: &Self::DecapsulationKey,
164191
) -> GenericArray<u8, Self::DecapsulationKeyLen> {
165-
GenericArray::clone_from_slice(key.as_bytes().as_slice())
192+
GenericArray::clone_from_slice(key.to_expanded_bytes().as_slice())
166193
}
167194

168195
fn deserialize_decapsulation_key(
169196
input: &mut &[u8],
170197
) -> Result<Self::DecapsulationKey, ProtocolError> {
171198
let bytes: GenericArray<u8, RcDecapsulationKeyLen<K>> =
172199
input.take_array("kem decapsulation key")?;
173-
let encoded = Encoded::<K::DecapsulationKey>::try_from(bytes.as_slice())
200+
let key = ml_kem::array::Array::try_from(bytes.as_slice())
174201
.map_err(|_| ProtocolError::SerializationError)?;
175-
Ok(K::DecapsulationKey::from_bytes(&encoded))
202+
K::DecapsulationKey::from_expanded_bytes(&key)
203+
.map_err(|_| ProtocolError::SerializationError)
176204
}
177205

178206
fn encapsulate<R: RngCore + CryptoRng>(
@@ -185,14 +213,11 @@ where
185213
),
186214
ProtocolError,
187215
> {
188-
key.encapsulate(rng)
189-
.map(|(ciphertext, shared)| {
190-
(
191-
GenericArray::clone_from_slice(ciphertext.as_slice()),
192-
GenericArray::clone_from_slice(shared.as_slice()),
193-
)
194-
})
195-
.map_err(|_| ProtocolError::LibraryError(InternalError::KemError))
216+
let (ciphertext, shared) = key.encapsulate_with_rng(&mut RngCompat(rng));
217+
Ok((
218+
GenericArray::clone_from_slice(ciphertext.as_slice()),
219+
GenericArray::clone_from_slice(shared.as_slice()),
220+
))
196221
}
197222

198223
fn decapsulate(
@@ -201,9 +226,8 @@ where
201226
) -> Result<GenericArray<u8, Self::SharedSecretLen>, ProtocolError> {
202227
let ciphertext = MlKemCiphertext::<K>::try_from(encapsulated_key.as_slice())
203228
.map_err(|_| ProtocolError::SerializationError)?;
204-
key.decapsulate(&ciphertext)
205-
.map(|shared| GenericArray::clone_from_slice(shared.as_slice()))
206-
.map_err(|_| ProtocolError::LibraryError(InternalError::KemError))
229+
let shared = key.decapsulate(&ciphertext);
230+
Ok(GenericArray::clone_from_slice(shared.as_slice()))
207231
}
208232
}
209233
/// Triple Diffie-Hellman-style key exchange that offloads the second hop to a

src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//!
1212
//! ### Minimum Supported Rust Version
1313
//!
14-
//! Rust **1.85** or higher.
14+
//! Rust **1.87** or higher.
1515
//!
1616
//! # Overview
1717
//!
@@ -1383,9 +1383,10 @@ mod tests;
13831383

13841384
#[cfg(feature = "argon2")]
13851385
pub use argon2;
1386+
pub use generic_array;
13861387
#[cfg(feature = "kem")]
13871388
pub use ml_kem;
1388-
pub use {generic_array, rand};
1389+
pub use rand;
13891390

13901391
pub use crate::ciphersuite::CipherSuite;
13911392
#[cfg(feature = "curve25519")]

0 commit comments

Comments
 (0)