Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define a Encapsulate::encapsulate_in_place method #1596

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions kem/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
use core::fmt::Debug;
use rand_core::CryptoRngCore;

/// A value that can be encapsulated to. Often, this will just be a public key. However, it can
/// also be a bundle of public keys, or it can include a sender's private key for authenticated
/// encapsulation.
/// This trait implements encapsulation. Often, the implementer will just be a public key. However,
/// it can also be a bundle of public keys, or it can include a sender's private key for
/// authenticated encapsulation.
pub trait Encapsulate<EK, SS> {
/// Encapsulation error
type Error: Debug;
Expand All @@ -23,9 +23,25 @@ pub trait Encapsulate<EK, SS> {
fn encapsulate(&self, rng: &mut impl CryptoRngCore) -> Result<(EK, SS), Self::Error>;
}

/// A value that can be used to decapsulate an encapsulated key. Often, this will just be a secret
/// key. But, as with [`Encapsulate`], it can be a bundle of secret keys, or it can include a
/// sender's private key for authenticated encapsulation.
/// This trait implements in-place encapsulation. Often, the implementer will just be a public key.
/// However, it can also be a bundle of public keys, or it can include a sender's private key for
/// authenticated encapsulation.
pub trait EncapsulateInPlace<EK, SS> {
/// Encapsulation error
type Error: Debug;

/// Encapsulates a fresh shared secret, placing the encapsulated key into the given mut ref.
/// If this errors, the final value of `encapsulated_key` MUST equal its original value.
fn encapsulate_in_place(
&self,
rng: &mut impl CryptoRngCore,
encapsulated_key: &mut EK,
) -> Result<SS, Self::Error>;
}

/// This trait implements decapsulation. Often, the implementer will just be a secret key. But, as
/// with [`Encapsulate`], it can be a bundle of secret keys, or it can include a sender's private
/// key for authenticated encapsulation.
pub trait Decapsulate<EK, SS> {
/// Decapsulation error
type Error: Debug;
Expand Down
4 changes: 3 additions & 1 deletion kem/tests/hpke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl Encapsulate<EncappedKey, SharedSecret> for PublicKey {
&self,
mut csprng: &mut impl CryptoRngCore,
) -> Result<(EncappedKey, SharedSecret), HpkeError> {
<X25519HkdfSha256 as KemTrait>::encap(&self.0, None, &mut csprng).map(|(ek, ss)| (ss, ek))
<X25519HkdfSha256 as KemTrait>::encap(&self.0, None, &mut csprng).map(|(ss, ek)| (ek, ss))
}
}

Expand Down Expand Up @@ -50,4 +50,6 @@ fn test_hpke() {
let (ek, ss1) = pk_recip.encapsulate(&mut rng).unwrap();
let ss2 = sk_recip.decapsulate(&ek).unwrap();
assert_eq!(ss1.0, ss2.0);

// Can't use encapsulate_in_place for this crate, because EncappedKey has no constructor
}
14 changes: 8 additions & 6 deletions kem/tests/saber.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use kem::{Decapsulate, Encapsulate};

use core::convert::Infallible;

use pqcrypto::kem::firesaber::{
decapsulate, encapsulate, keypair, Ciphertext as SaberEncappedKey, PublicKey, SecretKey,
SharedSecret as SaberSharedSecret,
Expand All @@ -12,23 +14,21 @@ struct SaberPublicKey(PublicKey);
struct SaberPrivateKey(SecretKey);

impl Encapsulate<SaberEncappedKey, SaberSharedSecret> for SaberPublicKey {
// TODO: Encapsulation is infallible. Make this the never type once it's available
type Error = ();
type Error = Infallible;

fn encapsulate(
&self,
_: &mut impl CryptoRngCore,
) -> Result<(SaberEncappedKey, SaberSharedSecret), ()> {
) -> Result<(SaberEncappedKey, SaberSharedSecret), Infallible> {
let (ss, ek) = encapsulate(&self.0);
Ok((ek, ss))
}
}

impl Decapsulate<SaberEncappedKey, SaberSharedSecret> for SaberPrivateKey {
// TODO: Decapsulation is infallible. Make this the never type once it's available
type Error = ();
type Error = Infallible;

fn decapsulate(&self, ek: &SaberEncappedKey) -> Result<SaberSharedSecret, ()> {
fn decapsulate(&self, ek: &SaberEncappedKey) -> Result<SaberSharedSecret, Infallible> {
Ok(decapsulate(ek, &self.0))
}
}
Expand All @@ -50,4 +50,6 @@ fn test_saber() {
let (ek, ss1) = pk_recip.encapsulate(&mut rng).unwrap();
let ss2 = sk_recip.decapsulate(&ek).unwrap();
assert_eq!(ss1.as_bytes(), ss2.as_bytes());

// Can't use encapsulate_in_place for this crate, because Ciphertext has no constructor
}
26 changes: 25 additions & 1 deletion kem/tests/x3dh.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use kem::{Decapsulate, Encapsulate};
use kem::{Decapsulate, Encapsulate, EncapsulateInPlace};

use p256::ecdsa::Signature;
use rand_core::CryptoRngCore;
Expand Down Expand Up @@ -70,6 +70,22 @@ impl Encapsulate<EphemeralKey, SharedSecret> for EncapContext {
}
}

// Same thing but in-place
impl EncapsulateInPlace<EphemeralKey, SharedSecret> for EncapContext {
type Error = &'static str;

// Do the dumb thing and just call encapsulate() and copy the result into the mut ref
fn encapsulate_in_place(
&self,
rng: &mut impl CryptoRngCore,
encapsulated_key: &mut EphemeralKey,
) -> Result<SharedSecret, Self::Error> {
let (ek, ss) = self.encapsulate(rng)?;
*encapsulated_key = ek;
Ok(ss)
}
}

// Define an decapsulator. Since authenticated and unauthenticated encapped keys are represented by
// the same type (which, outside of testing, should not be the case), this can do both auth'd and
// unauth'd decapsulation.
Expand Down Expand Up @@ -110,4 +126,12 @@ fn test_x3dh() {
let (encapped_key, ss1) = encap_context.encapsulate(&mut rng).unwrap();
let ss2 = decap_context.decapsulate(&encapped_key).unwrap();
assert_eq!(ss1, ss2);

// Now do the same but with encapsulate_in_place
let mut encapped_key = EphemeralKey::default();
let ss1 = encap_context
.encapsulate_in_place(&mut rng, &mut encapped_key)
.unwrap();
let ss2 = decap_context.decapsulate(&encapped_key).unwrap();
assert_eq!(ss1, ss2);
}