Skip to content

Commit 69a5030

Browse files
authored
Implement new kem::Kem trait (#223)
Companion PR to RustCrypto/traits#2243 This implements a trait which describes a whole KEM type family, with a similar shape to the former `dhkem::DhKem` and `ml_kem::KemCore` traits (both of which have been removed and replaced with `kem::Kem`). As part of this, the `*Params` types in `ml_kem` have been merged with the former type aliases of the `ml_kem::Kem` type (which have also been removed), and now `MlKem512`, `MlKem768`, and `MlKem1024` are the one true ZSTs for describing ML-KEM parameters.
1 parent 8f6cb8d commit 69a5030

File tree

21 files changed

+337
-373
lines changed

21 files changed

+337
-373
lines changed

Cargo.lock

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

dhkem/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ keywords = ["crypto", "ecdh", "ecc"]
1414
readme = "README.md"
1515

1616
[dependencies]
17-
kem = "0.3.0-rc.2"
17+
kem = "0.3.0-rc.3"
1818
rand_core = "0.10.0-rc-6"
1919

2020
# optional dependencies
@@ -29,7 +29,7 @@ zeroize = { version = "1.8.1", optional = true, default-features = false }
2929
[dev-dependencies]
3030
getrandom = { version = "0.4.0-rc.1", features = ["sys_rng"] }
3131
hex-literal = "1"
32-
hkdf = "0.13.0-rc.3"
32+
hkdf = "0.13.0-rc.4"
3333
sha2 = "0.11.0-rc.4"
3434

3535
[features]

dhkem/src/ecdh_kem.rs

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use elliptic_curve::{
1010
},
1111
};
1212
use kem::{
13-
Ciphertext, Encapsulate, Generate, InvalidKey, KemParams, KeyExport, KeySizeUser, SharedSecret,
13+
Ciphertext, Encapsulate, Generate, InvalidKey, Kem, KeyExport, KeySizeUser, SharedKey,
1414
TryDecapsulate, TryKeyInit,
1515
};
1616
use rand_core::{CryptoRng, TryCryptoRng};
@@ -29,15 +29,20 @@ pub type EcdhEncapsulationKey<C> = EncapsulationKey<PublicKey<C>>;
2929
/// traits from the `elliptic-curve` crate.
3030
///
3131
/// Implements a KEM interface that internally uses ECDH.
32+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
3233
pub struct EcdhKem<C: CurveArithmetic>(PhantomData<C>);
3334

34-
impl<C> KemParams for EcdhEncapsulationKey<C>
35+
impl<C> Kem for EcdhKem<C>
3536
where
3637
C: CurveArithmetic,
3738
FieldBytesSize<C>: ModulusSize,
39+
EcdhDecapsulationKey<C>: TryDecapsulate<Self> + Generate,
40+
EcdhEncapsulationKey<C>: Encapsulate<Self> + Clone,
3841
{
42+
type DecapsulationKey = EcdhDecapsulationKey<C>;
43+
type EncapsulationKey = EcdhEncapsulationKey<C>;
3944
type CiphertextSize = UncompressedPointSize<C>;
40-
type SharedSecretSize = FieldBytesSize<C>;
45+
type SharedKeySize = FieldBytesSize<C>;
4146
}
4247

4348
/// From [RFC9810 §7.1.1]: `SerializePublicKey` and `DeserializePublicKey`:
@@ -97,39 +102,39 @@ where
97102
}
98103
}
99104

100-
impl<C> Encapsulate for EcdhEncapsulationKey<C>
105+
impl<C> Generate for EcdhDecapsulationKey<C>
106+
where
107+
C: CurveArithmetic,
108+
FieldBytesSize<C>: ModulusSize,
109+
{
110+
fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
111+
Ok(EphemeralSecret::try_generate_from_rng(rng)?.into())
112+
}
113+
}
114+
115+
impl<C> Encapsulate<EcdhKem<C>> for EcdhEncapsulationKey<C>
101116
where
102117
C: CurveArithmetic,
103118
FieldBytesSize<C>: ModulusSize,
104119
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
105120
{
106-
fn encapsulate_with_rng<R>(&self, rng: &mut R) -> (Ciphertext<Self>, SharedSecret<Self>)
121+
fn encapsulate_with_rng<R>(
122+
&self,
123+
rng: &mut R,
124+
) -> (Ciphertext<EcdhKem<C>>, SharedKey<EcdhKem<C>>)
107125
where
108126
R: CryptoRng + ?Sized,
109127
{
110128
// ECDH encapsulation involves creating a new ephemeral key pair and then doing DH
111129
let sk = EphemeralSecret::generate_from_rng(rng);
112130
let ss = sk.diffie_hellman(&self.0);
113131

114-
// TODO(tarcieri): sk.public_key().to_uncompressed_point()
115-
let mut pk = UncompressedPoint::<C>::default();
116-
pk.copy_from_slice(sk.public_key().to_encoded_point(false).as_bytes());
117-
132+
let pk = sk.public_key().to_uncompressed_point();
118133
(pk, ss.raw_secret_bytes().clone())
119134
}
120135
}
121136

122-
impl<C> Generate for EcdhDecapsulationKey<C>
123-
where
124-
C: CurveArithmetic,
125-
FieldBytesSize<C>: ModulusSize,
126-
{
127-
fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
128-
Ok(EphemeralSecret::try_generate_from_rng(rng)?.into())
129-
}
130-
}
131-
132-
impl<C> TryDecapsulate for EcdhDecapsulationKey<C>
137+
impl<C> TryDecapsulate<EcdhKem<C>> for EcdhDecapsulationKey<C>
133138
where
134139
C: CurveArithmetic,
135140
FieldBytesSize<C>: ModulusSize,
@@ -139,8 +144,8 @@ where
139144

140145
fn try_decapsulate(
141146
&self,
142-
encapsulated_key: &Ciphertext<Self>,
143-
) -> Result<SharedSecret<Self>, Error> {
147+
encapsulated_key: &Ciphertext<EcdhKem<C>>,
148+
) -> Result<SharedKey<EcdhKem<C>>, Error> {
144149
let encapsulated_key = PublicKey::<C>::from_sec1_bytes(encapsulated_key)?;
145150
let shared_secret = self.dk.diffie_hellman(&encapsulated_key);
146151
Ok(shared_secret.raw_secret_bytes().clone())

dhkem/src/lib.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
//! [RFC9180]: https://datatracker.ietf.org/doc/html/rfc9180#name-dh-based-kem-dhkem
3131
//! [TLS KEM combiner]: https://datatracker.ietf.org/doc/html/draft-ietf-tls-hybrid-design-10
3232
33-
pub use kem::{self, Decapsulator, Encapsulate, Generate, KemParams, TryDecapsulate};
33+
pub use kem::{self, Encapsulate, Generate, Kem, TryDecapsulate};
3434

3535
#[cfg(feature = "ecdh")]
3636
mod ecdh_kem;
@@ -60,21 +60,15 @@ pub struct DecapsulationKey<DK, EK> {
6060
ek: EncapsulationKey<EK>,
6161
}
6262

63-
impl<DK, EK> Decapsulator for DecapsulationKey<DK, EK>
64-
where
65-
EncapsulationKey<EK>: Encapsulate + Clone,
66-
{
67-
type Encapsulator = EncapsulationKey<EK>;
68-
69-
fn encapsulator(&self) -> &EncapsulationKey<EK> {
63+
impl<DK, EK> AsRef<EncapsulationKey<EK>> for DecapsulationKey<DK, EK> {
64+
fn as_ref(&self) -> &EncapsulationKey<EK> {
7065
&self.ek
7166
}
7267
}
7368

7469
impl<DK, EK> From<DK> for DecapsulationKey<DK, EK>
7570
where
7671
EK: for<'a> From<&'a DK>,
77-
EncapsulationKey<EK>: KemParams,
7872
{
7973
fn from(dk: DK) -> Self {
8074
let ek = EncapsulationKey(EK::from(&dk));
@@ -140,27 +134,39 @@ impl<DK: Zeroize, EK> Zeroize for DecapsulationKey<DK, EK> {
140134
#[cfg(feature = "zeroize")]
141135
impl<DK: ZeroizeOnDrop, EK> ZeroizeOnDrop for DecapsulationKey<DK, EK> {}
142136

137+
/// NIST P-256 DHKEM.
138+
#[cfg(feature = "p256")]
139+
pub type NistP256Kem = EcdhKem<p256::NistP256>;
143140
/// NIST P-256 ECDH Decapsulation Key.
144141
#[cfg(feature = "p256")]
145142
pub type NistP256DecapsulationKey = EcdhDecapsulationKey<p256::NistP256>;
146143
/// NIST P-256 ECDH Encapsulation Key.
147144
#[cfg(feature = "p256")]
148145
pub type NistP256EncapsulationKey = EcdhEncapsulationKey<p256::NistP256>;
149146

147+
/// NIST P-256 DHKEM.
148+
#[cfg(feature = "p384")]
149+
pub type NistP384Kem = EcdhKem<p384::NistP384>;
150150
/// NIST P-384 ECDH Decapsulation Key.
151151
#[cfg(feature = "p384")]
152152
pub type NistP384DecapsulationKey = EcdhDecapsulationKey<p384::NistP384>;
153153
/// NIST P-384 ECDH Encapsulation Key.
154154
#[cfg(feature = "p384")]
155155
pub type NistP384EncapsulationKey = EcdhEncapsulationKey<p384::NistP384>;
156156

157+
/// NIST P-521 DHKEM.
158+
#[cfg(feature = "p521")]
159+
pub type NistP521Kem = EcdhKem<p521::NistP521>;
157160
/// NIST P-521 ECDH Decapsulation Key.
158161
#[cfg(feature = "p521")]
159162
pub type NistP521DecapsulationKey = EcdhDecapsulationKey<p521::NistP521>;
160163
/// NIST P-521 ECDH Encapsulation Key.
161164
#[cfg(feature = "p521")]
162165
pub type NistP521EncapsulationKey = EcdhEncapsulationKey<p521::NistP521>;
163166

167+
/// secp256k1 DHKEM.
168+
#[cfg(feature = "p521")]
169+
pub type Secp256k1Kem = EcdhKem<k256::Secp256k1>;
164170
/// secp256k1 ECDH Decapsulation Key.
165171
#[cfg(feature = "k256")]
166172
pub type Secp256k1DecapsulationKey = EcdhDecapsulationKey<k256::Secp256k1>;

dhkem/src/x25519_kem.rs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{DecapsulationKey, EncapsulationKey};
22
use kem::{
3-
Decapsulate, Encapsulate, Generate, InvalidKey, KemParams, Key, KeyExport, KeySizeUser,
4-
TryKeyInit, common::array::Array, consts::U32,
3+
Decapsulate, Encapsulate, Generate, InvalidKey, Kem, Key, KeyExport, KeySizeUser, TryKeyInit,
4+
common::array::Array, consts::U32,
55
};
66
use rand_core::{CryptoRng, TryCryptoRng, UnwrapErr};
77
use x25519::{PublicKey, ReusableSecret};
@@ -20,16 +20,19 @@ pub type X25519EncapsulationKey = EncapsulationKey<PublicKey>;
2020
type Ciphertext = Array<u8, U32>;
2121

2222
/// X25519 shared secrets are also compressed Montgomery x/u-coordinates.
23-
type SharedSecret = Array<u8, U32>;
23+
type SharedKey = Array<u8, U32>;
2424

2525
/// X22519 Diffie-Hellman KEM adapter.
2626
///
2727
/// Implements a KEM interface that internally uses X25519 ECDH.
28+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
2829
pub struct X25519Kem;
2930

30-
impl KemParams for EncapsulationKey<PublicKey> {
31+
impl Kem for X25519Kem {
32+
type DecapsulationKey = X25519DecapsulationKey;
33+
type EncapsulationKey = X25519EncapsulationKey;
3134
type CiphertextSize = U32;
32-
type SharedSecretSize = U32;
35+
type SharedKeySize = U32;
3336
}
3437

3538
/// From [RFC9810 §7.1.1]: `SerializePublicKey` and `DeserializePublicKey`:
@@ -69,8 +72,17 @@ impl KeyExport for X25519EncapsulationKey {
6972
}
7073
}
7174

72-
impl Encapsulate for X25519EncapsulationKey {
73-
fn encapsulate_with_rng<R>(&self, rng: &mut R) -> (Ciphertext, SharedSecret)
75+
impl Generate for X25519DecapsulationKey {
76+
fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
77+
// TODO(tarcieri): don't panic! Fallible `ReusableSecret` generation?
78+
Ok(Self::from(ReusableSecret::random_from_rng(&mut UnwrapErr(
79+
rng,
80+
))))
81+
}
82+
}
83+
84+
impl Encapsulate<X25519Kem> for X25519EncapsulationKey {
85+
fn encapsulate_with_rng<R>(&self, rng: &mut R) -> (Ciphertext, SharedKey)
7486
where
7587
R: CryptoRng + ?Sized,
7688
{
@@ -82,17 +94,8 @@ impl Encapsulate for X25519EncapsulationKey {
8294
}
8395
}
8496

85-
impl Generate for X25519DecapsulationKey {
86-
fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
87-
// TODO(tarcieri): don't panic! Fallible `ReusableSecret` generation?
88-
Ok(Self::from(ReusableSecret::random_from_rng(&mut UnwrapErr(
89-
rng,
90-
))))
91-
}
92-
}
93-
94-
impl Decapsulate for X25519DecapsulationKey {
95-
fn decapsulate(&self, encapsulated_key: &Ciphertext) -> SharedSecret {
97+
impl Decapsulate<X25519Kem> for X25519DecapsulationKey {
98+
fn decapsulate(&self, encapsulated_key: &Ciphertext) -> SharedKey {
9699
let public_key = PublicKey::from(encapsulated_key.0);
97100
self.dk.diffie_hellman(&public_key).to_bytes().into()
98101
}

dhkem/tests/hpke_p256_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use dhkem::NistP256DecapsulationKey;
55
use elliptic_curve::Generate;
66
use hex_literal::hex;
77
use hkdf::Hkdf;
8-
use kem::{Decapsulator, Encapsulate, KeyExport, TryDecapsulate};
8+
use kem::{Encapsulate, KeyExport, TryDecapsulate};
99
use rand_core::{TryCryptoRng, TryRng};
1010
use sha2::Sha256;
1111

@@ -83,7 +83,7 @@ fn test_dhkem_p256_hkdf_sha256() {
8383
"f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2"
8484
)))
8585
.unwrap();
86-
let pkr = skr.encapsulator();
86+
let pkr = skr.as_ref();
8787
assert_eq!(&pkr.to_bytes(), &example_pkr);
8888

8989
let (pke, ss1) = pkr.encapsulate_with_rng(&mut ConstantRng(&hex!(

dhkem/tests/tests.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
feature = "x25519"
77
))]
88

9-
use kem::{Decapsulator, Encapsulate, Generate, TryDecapsulate};
9+
use kem::{Encapsulate, Generate, Kem, TryDecapsulate};
1010

11-
fn test_kem<DK: Decapsulator + Generate + TryDecapsulate>() {
12-
let dk = DK::generate();
13-
let ek = dk.encapsulator();
11+
fn test_kem<K: Kem>() {
12+
let dk = K::DecapsulationKey::generate();
13+
let ek = dk.as_ref().clone();
1414
let (ek, ss1) = ek.encapsulate();
1515
let ss2 = dk.try_decapsulate(&ek).unwrap();
1616
assert_eq!(ss1.as_slice(), ss2.as_slice());
@@ -19,29 +19,29 @@ fn test_kem<DK: Decapsulator + Generate + TryDecapsulate>() {
1919
#[cfg(feature = "x25519")]
2020
#[test]
2121
fn test_x25519() {
22-
test_kem::<dhkem::X25519DecapsulationKey>();
22+
test_kem::<dhkem::X25519Kem>();
2323
}
2424

2525
#[cfg(feature = "k256")]
2626
#[test]
2727
fn test_k256() {
28-
test_kem::<dhkem::Secp256k1DecapsulationKey>();
28+
test_kem::<dhkem::Secp256k1Kem>();
2929
}
3030

3131
#[cfg(feature = "p256")]
3232
#[test]
3333
fn test_p256() {
34-
test_kem::<dhkem::NistP256DecapsulationKey>();
34+
test_kem::<dhkem::NistP256Kem>();
3535
}
3636

3737
#[cfg(feature = "p384")]
3838
#[test]
3939
fn test_p384() {
40-
test_kem::<dhkem::NistP384DecapsulationKey>();
40+
test_kem::<dhkem::NistP384Kem>();
4141
}
4242

4343
#[cfg(feature = "p521")]
4444
#[test]
4545
fn test_p521() {
46-
test_kem::<dhkem::NistP521DecapsulationKey>();
46+
test_kem::<dhkem::NistP521Kem>();
4747
}

0 commit comments

Comments
 (0)