Skip to content

Commit 3ae0d5f

Browse files
committed
kem: use byte arrays for EK and SS
Companion PR to RustCrypto/traits#2220 Changes the `Encapsulate` and `Decapsulate` traits to operate on byte arrays instead of generic type parameters. This means all the impls must fully decode their inputs from bytes and encode their outputs to bytes. `dhkem` was a challenge here since the ciphertext is a curve point and decoding it may result in an error, so RustCrypto/traits#2220 also added a `TryDecapsulate` trait that's fallible (like the trait API was before, but nothing actually needed it until now). This commit also migrates most of the tests over to using the `kem` traits rather than bespoke traits defined within algorithm-specific crates. Hopefully soon we can just get rid of the latter.
1 parent 1ce3538 commit 3ae0d5f

16 files changed

Lines changed: 454 additions & 337 deletions

File tree

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ debug = true
1313
[patch.crates-io]
1414
ml-kem = { path = "./ml-kem" }
1515

16-
kem = { git = "https://github.com/RustCrypto/traits" }
17-
sec1 = { git = "https://github.com/RustCrypto/formats" }
16+
elliptic-curve = { git = "https://github.com/RustCrypto/traits" }
17+
kem = { git = "https://github.com/RustCrypto/traits", branch = "kem/use-byte-arrays-for-ek-and-ss" }

dhkem/src/ecdh_kem.rs

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,45 @@
11
//! Generic Elliptic Curve Diffie-Hellman KEM adapter.
22
3-
use crate::{DhDecapsulator, DhEncapsulator, DhKem};
3+
use crate::{DecapsulationKey, DhKem, EncapsulationKey};
44
use core::marker::PhantomData;
55
use elliptic_curve::{
6-
AffinePoint, CurveArithmetic, FieldBytesSize, Generate, PublicKey,
7-
ecdh::{EphemeralSecret, SharedSecret},
6+
AffinePoint, CurveArithmetic, Error, FieldBytesSize, PublicKey,
7+
ecdh::EphemeralSecret,
88
sec1::{
99
FromEncodedPoint, ModulusSize, ToEncodedPoint, UncompressedPoint, UncompressedPointSize,
1010
},
1111
};
12-
use kem::{Decapsulate, Encapsulate, InvalidKey, KeyExport, KeySizeUser, TryKeyInit};
12+
use kem::{
13+
Ciphertext, Encapsulate, Generate, InvalidKey, Kem, KeyExport, KeySizeUser, SharedSecret,
14+
TryDecapsulate, TryKeyInit,
15+
};
1316
use rand_core::{CryptoRng, TryCryptoRng};
1417

18+
/// Elliptic Curve Diffie-Hellman Decapsulation Key (i.e. secret decryption key)
19+
///
20+
/// Generic around an elliptic curve `C`.
21+
pub type EcdhDecapsulationKey<C> = DecapsulationKey<EphemeralSecret<C>, PublicKey<C>>;
22+
23+
/// Elliptic Curve Diffie-Hellman Encapsulation Key (i.e. public encryption key)
24+
///
25+
/// Generic around an elliptic curve `C`.
26+
pub type EcdhEncapsulationKey<C> = EncapsulationKey<PublicKey<C>>;
27+
1528
/// Generic Elliptic Curve Diffie-Hellman KEM adapter compatible with curves implemented using
1629
/// traits from the `elliptic-curve` crate.
1730
///
1831
/// Implements a KEM interface that internally uses ECDH.
1932
pub struct EcdhKem<C: CurveArithmetic>(PhantomData<C>);
2033

34+
impl<C> Kem for EcdhEncapsulationKey<C>
35+
where
36+
C: CurveArithmetic,
37+
FieldBytesSize<C>: ModulusSize,
38+
{
39+
type CiphertextSize = UncompressedPointSize<C>;
40+
type SharedSecretSize = FieldBytesSize<C>;
41+
}
42+
2143
/// From [RFC9810 §7.1.1]: `SerializePublicKey` and `DeserializePublicKey`:
2244
///
2345
/// > For P-256, P-384, and P-521, the SerializePublicKey() function of the
@@ -26,7 +48,7 @@ pub struct EcdhKem<C: CurveArithmetic>(PhantomData<C>);
2648
///
2749
/// [RFC9810 §7.1.1]: https://datatracker.ietf.org/doc/html/rfc9180#name-serializepublickey-and-dese
2850
/// [SECG]: https://www.secg.org/sec1-v2.pdf
29-
impl<C> KeySizeUser for DhEncapsulator<PublicKey<C>>
51+
impl<C> KeySizeUser for EcdhEncapsulationKey<C>
3052
where
3153
C: CurveArithmetic,
3254
FieldBytesSize<C>: ModulusSize,
@@ -40,7 +62,7 @@ where
4062
/// > Octet-String-to-Elliptic-Curve-Point conversion.
4163
///
4264
/// [RFC9810 §7.1.1]: https://datatracker.ietf.org/doc/html/rfc9180#name-serializepublickey-and-dese
43-
impl<C> TryKeyInit for DhEncapsulator<PublicKey<C>>
65+
impl<C> TryKeyInit for EcdhEncapsulationKey<C>
4466
where
4567
C: CurveArithmetic,
4668
FieldBytesSize<C>: ModulusSize,
@@ -61,21 +83,18 @@ where
6183
///
6284
/// [RFC9810 §7.1.1]: https://datatracker.ietf.org/doc/html/rfc9180#name-serializepublickey-and-dese
6385
/// [SECG]: https://www.secg.org/sec1-v2.pdf
64-
impl<C> KeyExport for DhEncapsulator<PublicKey<C>>
86+
impl<C> KeyExport for EcdhEncapsulationKey<C>
6587
where
6688
C: CurveArithmetic,
6789
FieldBytesSize<C>: ModulusSize,
6890
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
6991
{
7092
fn to_bytes(&self) -> UncompressedPoint<C> {
71-
// TODO(tarcieri): use `ToEncodedPoint::to_uncompressed_point` (RustCrypto/traits#2221)
72-
let mut ret = UncompressedPoint::<C>::default();
73-
ret.copy_from_slice(self.0.to_encoded_point(false).as_bytes());
74-
ret
93+
self.0.to_uncompressed_point()
7594
}
7695
}
7796

78-
impl<C> Encapsulate<PublicKey<C>, SharedSecret<C>> for DhEncapsulator<PublicKey<C>>
97+
impl<C> Encapsulate for EcdhEncapsulationKey<C>
7998
where
8099
C: CurveArithmetic,
81100
FieldBytesSize<C>: ModulusSize,
@@ -84,31 +103,42 @@ where
84103
fn encapsulate_with_rng<R: TryCryptoRng + ?Sized>(
85104
&self,
86105
rng: &mut R,
87-
) -> Result<(PublicKey<C>, SharedSecret<C>), R::Error> {
106+
) -> Result<(Ciphertext<Self>, SharedSecret<Self>), R::Error> {
88107
// ECDH encapsulation involves creating a new ephemeral key pair and then doing DH
89108
// TODO(tarcieri): propagate RNG errors
90109
let sk = EphemeralSecret::try_generate_from_rng(rng).expect("RNG failure");
91110
let pk = sk.public_key();
92111
let ss = sk.diffie_hellman(&self.0);
93112

94-
Ok((pk, ss))
113+
Ok((pk.to_uncompressed_point(), ss.raw_secret_bytes().clone()))
95114
}
96115
}
97116

98-
impl<C> Decapsulate<PublicKey<C>, SharedSecret<C>> for DhDecapsulator<EphemeralSecret<C>>
117+
impl<C> Generate for EcdhDecapsulationKey<C>
99118
where
100119
C: CurveArithmetic,
101120
FieldBytesSize<C>: ModulusSize,
102-
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
103121
{
104-
type Encapsulator = DhEncapsulator<PublicKey<C>>;
105-
106-
fn decapsulate(&self, encapsulated_key: &PublicKey<C>) -> SharedSecret<C> {
107-
self.0.diffie_hellman(encapsulated_key)
122+
fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
123+
Ok(EphemeralSecret::try_generate_from_rng(rng)?.into())
108124
}
125+
}
126+
127+
impl<C> TryDecapsulate for EcdhDecapsulationKey<C>
128+
where
129+
C: CurveArithmetic,
130+
FieldBytesSize<C>: ModulusSize,
131+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
132+
{
133+
type Error = Error;
109134

110-
fn encapsulator(&self) -> DhEncapsulator<PublicKey<C>> {
111-
DhEncapsulator(self.0.public_key())
135+
fn try_decapsulate(
136+
&self,
137+
encapsulated_key: &Ciphertext<Self>,
138+
) -> Result<SharedSecret<Self>, Error> {
139+
let encapsulated_key = PublicKey::<C>::from_sec1_bytes(encapsulated_key)?;
140+
let shared_secret = self.dk.diffie_hellman(&encapsulated_key);
141+
Ok(shared_secret.raw_secret_bytes().clone())
112142
}
113143
}
114144

@@ -118,10 +148,10 @@ where
118148
FieldBytesSize<C>: ModulusSize,
119149
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
120150
{
121-
type DecapsulatingKey = DhDecapsulator<EphemeralSecret<C>>;
122-
type EncapsulatingKey = DhEncapsulator<PublicKey<C>>;
123-
type EncapsulatedKey = PublicKey<C>;
124-
type SharedSecret = SharedSecret<C>;
151+
type DecapsulatingKey = EcdhDecapsulationKey<C>;
152+
type EncapsulatingKey = EcdhEncapsulationKey<C>;
153+
type EncapsulatedKey = Ciphertext<EcdhDecapsulationKey<C>>;
154+
type SharedSecret = SharedSecret<EcdhDecapsulationKey<C>>;
125155

126156
fn random_keypair<R: CryptoRng + ?Sized>(
127157
rng: &mut R,
@@ -130,6 +160,6 @@ where
130160
let sk = EphemeralSecret::try_generate_from_rng(rng).expect("RNG failure");
131161
let pk = PublicKey::from(&sk);
132162

133-
(DhDecapsulator(sk), DhEncapsulator(pk))
163+
(DecapsulationKey::from(sk), EncapsulationKey(pk))
134164
}
135165
}

0 commit comments

Comments
 (0)