From 58424cfa4e7d2c5fd8b387ae9f424bfb12f30c13 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 5 Feb 2026 08:48:36 -0700 Subject: [PATCH] Remove generics from `*Decapsulate`/`Encapsulate` Companion PR to RustCrypto/traits#2282 --- Cargo.lock | 7 +++---- Cargo.toml | 2 ++ dhkem/src/ecdh_kem.rs | 27 +++++++++++++++++++++------ dhkem/src/lib.rs | 16 ++++++++-------- dhkem/src/x25519_kem.rs | 18 ++++++++++++++---- dhkem/tests/tests.rs | 20 +++++++++++--------- ml-kem/src/decapsulation_key.rs | 18 +++++++++++------- ml-kem/src/encapsulation_key.rs | 4 +++- ml-kem/src/lib.rs | 3 +-- ml-kem/tests/encap-decap.rs | 7 +++---- x-wing/src/lib.rs | 24 ++++++++++++++---------- 11 files changed, 91 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef88697..9f3e5bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,9 +314,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.0-rc.15" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8441110cea75afde0b89a8d796e2bc67b23432f5a9566cb15d9d365d91a2b0" +checksum = "211f05e03c7d03754740fd9e585de910a095d6b99f8bcfffdef8319fa02a8331" dependencies = [ "getrandom", "hybrid-array", @@ -732,8 +732,7 @@ dependencies = [ [[package]] name = "kem" version = "0.3.0-rc.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c154411ffe1b199ef0b7e209e0cd2f05460aa5b9483873979042bfb02d0addd" +source = "git+https://github.com/RustCrypto/traits#62a42bccc4d0d3934a0d7e9e737127fab8585e83" dependencies = [ "crypto-common", "rand_core", diff --git a/Cargo.toml b/Cargo.toml index a5dedb5..2a2fd5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,3 +58,5 @@ debug = true [patch.crates-io] ml-kem = { path = "./ml-kem" } module-lattice = { path = "./module-lattice" } + +kem = { git = "https://github.com/RustCrypto/traits" } diff --git a/dhkem/src/ecdh_kem.rs b/dhkem/src/ecdh_kem.rs index a220c74..89100a3 100644 --- a/dhkem/src/ecdh_kem.rs +++ b/dhkem/src/ecdh_kem.rs @@ -8,8 +8,8 @@ use elliptic_curve::{ sec1::{FromSec1Point, ModulusSize, ToSec1Point, UncompressedPoint, UncompressedPointSize}, }; use kem::{ - Ciphertext, Encapsulate, Generate, InvalidKey, Kem, KeyExport, KeySizeUser, SharedKey, - TryDecapsulate, TryKeyInit, + Ciphertext, Decapsulator, Encapsulate, Generate, InvalidKey, Kem, KeyExport, KeySizeUser, + SharedKey, TryDecapsulate, TryKeyInit, }; use rand_core::{CryptoRng, TryCryptoRng}; @@ -27,8 +27,8 @@ impl Kem for EcdhKem where C: CurveArithmetic, FieldBytesSize: ModulusSize, - EcdhDecapsulationKey: TryDecapsulate + Generate, - EcdhEncapsulationKey: Encapsulate + Clone, + EcdhDecapsulationKey: TryDecapsulate + Generate, + EcdhEncapsulationKey: Encapsulate + Clone, { type DecapsulationKey = EcdhDecapsulationKey; type EncapsulationKey = EcdhEncapsulationKey; @@ -41,6 +41,19 @@ where /// Generic around an elliptic curve `C`. pub type EcdhDecapsulationKey = DecapsulationKey, PublicKey>; +impl Decapsulator for EcdhDecapsulationKey +where + C: CurveArithmetic, + FieldBytesSize: ModulusSize, + AffinePoint: FromSec1Point + ToSec1Point, +{ + type Kem = EcdhKem; + + fn encapsulation_key(&self) -> &EcdhEncapsulationKey { + &self.ek + } +} + impl KeySizeUser for EcdhDecapsulationKey where C: CurveArithmetic, @@ -103,7 +116,7 @@ where /// To produce something suitable for e.g. symmetric key(s), use the [`Expander`] type to derive /// output keys. /// -impl TryDecapsulate> for EcdhDecapsulationKey +impl TryDecapsulate for EcdhDecapsulationKey where C: CurveArithmetic, FieldBytesSize: ModulusSize, @@ -189,12 +202,14 @@ where /// To produce something suitable for e.g. symmetric key(s), use the [`Expander`] type to derive /// output keys. /// -impl Encapsulate> for EcdhEncapsulationKey +impl Encapsulate for EcdhEncapsulationKey where C: CurveArithmetic, FieldBytesSize: ModulusSize, AffinePoint: FromSec1Point + ToSec1Point, { + type Kem = EcdhKem; + fn encapsulate_with_rng( &self, rng: &mut R, diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index b8ef932..4882b76 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -32,7 +32,7 @@ mod expander; pub use expander::{Expander, InvalidLength}; -pub use kem::{self, Encapsulate, Generate, Kem, TryDecapsulate}; +pub use kem::{self, Decapsulator, Encapsulate, Generate, Kem, TryDecapsulate}; #[cfg(feature = "ecdh")] mod ecdh_kem; @@ -62,6 +62,13 @@ pub struct DecapsulationKey { ek: EncapsulationKey, } +impl DecapsulationKey { + /// Consumes `self` and returns the wrapped value + pub fn into_inner(self) -> DK { + self.dk + } +} + impl AsRef> for DecapsulationKey { fn as_ref(&self) -> &EncapsulationKey { &self.ek @@ -78,13 +85,6 @@ where } } -impl DecapsulationKey { - /// Consumes `self` and returns the wrapped value - pub fn into_inner(self) -> DK { - self.dk - } -} - /// Newtype for a piece of data that may be encapsulated #[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Default)] pub struct EncapsulationKey(EK); diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs index 5c7a684..020c217 100644 --- a/dhkem/src/x25519_kem.rs +++ b/dhkem/src/x25519_kem.rs @@ -1,7 +1,7 @@ use crate::{DecapsulationKey, EncapsulationKey}; use kem::{ - Decapsulate, Encapsulate, Generate, InvalidKey, Kem, Key, KeyExport, KeyInit, KeySizeUser, - TryKeyInit, + Decapsulate, Decapsulator, Encapsulate, Generate, InvalidKey, Kem, Key, KeyExport, KeyInit, + KeySizeUser, TryKeyInit, common::array::{Array, sizes::U32}, }; use rand_core::{CryptoRng, TryCryptoRng}; @@ -34,6 +34,14 @@ impl Kem for X25519Kem { /// Generic around an elliptic curve `C`. pub type X25519DecapsulationKey = DecapsulationKey; +impl Decapsulator for X25519DecapsulationKey { + type Kem = X25519Kem; + + fn encapsulation_key(&self) -> &X25519EncapsulationKey { + &self.ek + } +} + impl KeySizeUser for X25519DecapsulationKey { type KeySize = U32; } @@ -79,7 +87,7 @@ impl Generate for X25519DecapsulationKey { /// To produce something suitable for e.g. symmetric key(s), use the [`Expander`] type to derive /// output keys. /// -impl Decapsulate for X25519DecapsulationKey { +impl Decapsulate for X25519DecapsulationKey { fn decapsulate(&self, encapsulated_key: &Ciphertext) -> SharedKey { let public_key = PublicKey::from(encapsulated_key.0); self.dk.diffie_hellman(&public_key).to_bytes().into() @@ -137,7 +145,9 @@ impl KeyExport for X25519EncapsulationKey { /// To produce something suitable for e.g. symmetric key(s), use the [`Expander`] type to derive /// output keys. /// -impl Encapsulate for X25519EncapsulationKey { +impl Encapsulate for X25519EncapsulationKey { + type Kem = X25519Kem; + fn encapsulate_with_rng(&self, rng: &mut R) -> (Ciphertext, SharedKey) where R: CryptoRng + ?Sized, diff --git a/dhkem/tests/tests.rs b/dhkem/tests/tests.rs index cf4f2f2..3ea97b6 100644 --- a/dhkem/tests/tests.rs +++ b/dhkem/tests/tests.rs @@ -1,19 +1,21 @@ //! DHKEM tests. -#![cfg(any( - feature = "k256", - feature = "p256", - feature = "p384", - feature = "p521", - feature = "x25519" +#![cfg(all( + feature = "getrandom", + any( + feature = "k256", + feature = "p256", + feature = "p384", + feature = "p521", + feature = "x25519" + ) ))] #![allow(clippy::unwrap_used, reason = "tests")] -use kem::{Encapsulate, Generate, Kem, TryDecapsulate}; +use kem::{Encapsulate, Kem, TryDecapsulate}; fn test_kem() { - let dk = K::DecapsulationKey::generate(); - let ek = dk.as_ref().clone(); + let (dk, ek) = K::generate_keypair(); let (ek, ss1) = ek.encapsulate(); let ss2 = dk.try_decapsulate(&ek).unwrap(); assert_eq!(ss1.as_slice(), ss2.as_slice()); diff --git a/ml-kem/src/decapsulation_key.rs b/ml-kem/src/decapsulation_key.rs index 30571a3..e3bcf13 100644 --- a/ml-kem/src/decapsulation_key.rs +++ b/ml-kem/src/decapsulation_key.rs @@ -1,7 +1,6 @@ use crate::{ B32, EncapsulationKey, Seed, SharedKey, crypto::{G, J}, - kem::{Generate, InvalidKey, Kem, KeyExport, KeyInit, KeySizeUser}, param::{DecapsulationKeySize, ExpandedDecapsulationKey, KemParams}, pke::{DecryptionKey, EncryptionKey}, }; @@ -9,7 +8,10 @@ use array::{ Array, ArraySize, sizes::{U32, U64}, }; -use kem::{Ciphertext, Decapsulate}; +use kem::{ + Ciphertext, Decapsulate, Decapsulator, Generate, InvalidKey, Kem, KeyExport, KeyInit, + KeySizeUser, +}; use rand_core::{TryCryptoRng, TryRng}; use subtle::{ConditionallySelectable, ConstantTimeEq}; @@ -145,7 +147,7 @@ where } } -impl

Decapsulate

for DecapsulationKey

+impl

Decapsulate for DecapsulationKey

where P: Kem, SharedKeySize = U32> + KemParams, { @@ -158,11 +160,13 @@ where } } -impl

AsRef> for DecapsulationKey

+impl

Decapsulator for DecapsulationKey

where - P: KemParams, + P: Kem, SharedKeySize = U32> + KemParams, { - fn as_ref(&self) -> &EncapsulationKey

{ + type Kem = P; + + fn encapsulation_key(&self) -> &EncapsulationKey

{ &self.ek } } @@ -271,7 +275,7 @@ where { fn from_seed(seed: &Seed) -> (K::DecapsulationKey, K::EncapsulationKey) { let dk = K::DecapsulationKey::new(seed); - let ek = dk.as_ref().clone(); + let ek = dk.encapsulation_key().clone(); (dk, ek) } } diff --git a/ml-kem/src/encapsulation_key.rs b/ml-kem/src/encapsulation_key.rs index 58ee4e9..f09e246 100644 --- a/ml-kem/src/encapsulation_key.rs +++ b/ml-kem/src/encapsulation_key.rs @@ -63,10 +63,12 @@ where } } -impl

Encapsulate

for EncapsulationKey

+impl

Encapsulate for EncapsulationKey

where P: Kem + KemParams, { + type Kem = P; + fn encapsulate_with_rng(&self, rng: &mut R) -> (Ciphertext

, SharedKey) where R: CryptoRng + ?Sized, diff --git a/ml-kem/src/lib.rs b/ml-kem/src/lib.rs index 6a2dc10..d41bea5 100644 --- a/ml-kem/src/lib.rs +++ b/ml-kem/src/lib.rs @@ -278,8 +278,7 @@ mod test { where K: Kem, { - let dk = K::DecapsulationKey::generate(); - let ek = dk.as_ref().clone(); + let (dk, ek) = K::generate_keypair(); let (ct, k_send) = ek.encapsulate(); let k_recv = dk.try_decapsulate(&ct).unwrap(); assert_eq!(k_send, k_recv); diff --git a/ml-kem/tests/encap-decap.rs b/ml-kem/tests/encap-decap.rs index 517d32c..4109f27 100644 --- a/ml-kem/tests/encap-decap.rs +++ b/ml-kem/tests/encap-decap.rs @@ -3,8 +3,8 @@ #![allow(unreachable_pub, reason = "tests")] #![allow(clippy::unwrap_used, reason = "tests")] -use ::kem::Decapsulate; use array::{Array, ArrayN}; +use kem::{Ciphertext, Decapsulate, Decapsulator}; use ml_kem::*; use std::{fs::read_to_string, path::PathBuf}; @@ -92,11 +92,10 @@ fn verify_decap_group(tg: &acvp::DecapTestGroup) { fn verify_decap(tc: &acvp::DecapTestCase, dk_slice: &[u8]) where K: Kem, - K::DecapsulationKey: Decapsulate + ExpandedKeyEncoding, + K::DecapsulationKey: Decapsulate + Decapsulator + ExpandedKeyEncoding, { let dk = K::DecapsulationKey::from_expanded_bytes(dk_slice.try_into().unwrap()).unwrap(); - - let c = ::kem::Ciphertext::::try_from(tc.c.as_slice()).unwrap(); + let c = Ciphertext::::try_from(tc.c.as_slice()).unwrap(); let k = dk.decapsulate(&c); assert_eq!(k.as_slice(), tc.k.as_slice()); } diff --git a/x-wing/src/lib.rs b/x-wing/src/lib.rs index 2e5dc38..db4d7c9 100644 --- a/x-wing/src/lib.rs +++ b/x-wing/src/lib.rs @@ -29,8 +29,8 @@ //! ``` pub use kem::{ - self, Decapsulate, Encapsulate, Generate, InvalidKey, Kem, Key, KeyExport, KeyInit, - KeySizeUser, TryKeyInit, + self, Decapsulate, Decapsulator, Encapsulate, Generate, InvalidKey, Kem, Key, KeyExport, + KeyInit, KeySizeUser, TryKeyInit, }; use core::fmt::{self, Debug}; @@ -132,7 +132,9 @@ impl EncapsulationKey { } } -impl Encapsulate for EncapsulationKey { +impl Encapsulate for EncapsulationKey { + type Kem = XWingKem; + fn encapsulate_with_rng(&self, rng: &mut R) -> (Ciphertext, SharedKey) where R: CryptoRng + ?Sized, @@ -196,12 +198,6 @@ impl DecapsulationKey { } } -impl AsRef for DecapsulationKey { - fn as_ref(&self) -> &EncapsulationKey { - &self.ek - } -} - impl Debug for DecapsulationKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DecapsulationKey") @@ -210,7 +206,7 @@ impl Debug for DecapsulationKey { } } -impl Decapsulate for DecapsulationKey { +impl Decapsulate for DecapsulationKey { #[allow(clippy::similar_names)] // So we can use the names as in the RFC fn decapsulate(&self, ct: &Ciphertext) -> SharedKey { let ct = CiphertextMessage::from(ct); @@ -225,6 +221,14 @@ impl Decapsulate for DecapsulationKey { } } +impl Decapsulator for DecapsulationKey { + type Kem = XWingKem; + + fn encapsulation_key(&self) -> &EncapsulationKey { + &self.ek + } +} + impl Drop for DecapsulationKey { fn drop(&mut self) { #[cfg(feature = "zeroize")]