use crate::{
aead::{Aead, AeadTag},
kdf::Kdf as KdfTrait,
kem::{EncappedKey, Kem as KemTrait},
kex::KeyExchange,
op_mode::{OpModeR, OpModeS},
setup::{setup_receiver, setup_sender},
HpkeError,
};
use rand::{CryptoRng, RngCore};
pub fn single_shot_seal<A, Kdf, Kem, R>(
mode: &OpModeS<Kem::Kex>,
pk_recip: &<Kem::Kex as KeyExchange>::PublicKey,
info: &[u8],
plaintext: &mut [u8],
aad: &[u8],
csprng: &mut R,
) -> Result<(EncappedKey<Kem::Kex>, AeadTag<A>), HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
R: CryptoRng + RngCore,
{
let (encapped_key, mut aead_ctx) =
setup_sender::<A, Kdf, Kem, R>(mode, pk_recip, info, csprng)?;
let tag = aead_ctx.seal(plaintext, aad)?;
Ok((encapped_key, tag))
}
pub fn single_shot_open<A, Kdf, Kem>(
mode: &OpModeR<Kem::Kex>,
sk_recip: &<Kem::Kex as KeyExchange>::PrivateKey,
encapped_key: &EncappedKey<Kem::Kex>,
info: &[u8],
ciphertext: &mut [u8],
aad: &[u8],
tag: &AeadTag<A>,
) -> Result<(), HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
{
let mut aead_ctx = setup_receiver::<A, Kdf, Kem>(mode, sk_recip, encapped_key, info)?;
aead_ctx.open(ciphertext, aad, tag)
}
#[cfg(test)]
mod test {
use super::{single_shot_open, single_shot_seal};
use crate::{
aead::ChaCha20Poly1305,
kdf::HkdfSha256,
kem::Kem as KemTrait,
kex::KeyExchange,
op_mode::{OpModeR, OpModeS, PskBundle},
test_util::gen_rand_buf,
};
use rand::{rngs::StdRng, SeedableRng};
macro_rules! test_single_shot_correctness {
($test_name:ident, $aead:ty, $kdf:ty, $kem:ty) => {
#[test]
fn $test_name() {
type A = $aead;
type Kdf = $kdf;
type Kem = $kem;
type Kex = <Kem as KemTrait>::Kex;
let msg = b"Good night, a-ding ding ding ding ding";
let aad = b"Five four three two one";
let mut csprng = StdRng::from_entropy();
let info = b"why would you think in a million years that that would actually work";
let (psk, psk_id) = (gen_rand_buf(), gen_rand_buf());
let psk_bundle = PskBundle {
psk: &psk,
psk_id: &psk_id,
};
let (sk_sender_id, pk_sender_id) = Kex::gen_keypair(&mut csprng);
let (sk_recip, pk_recip) = Kex::gen_keypair(&mut csprng);
let sender_mode = OpModeS::<Kex>::AuthPsk(
(sk_sender_id, pk_sender_id.clone()),
psk_bundle.clone(),
);
let receiver_mode = OpModeR::<Kex>::AuthPsk(pk_sender_id, psk_bundle);
let mut ciphertext = msg.clone();
let (encapped_key, tag) = single_shot_seal::<A, Kdf, Kem, _>(
&sender_mode,
&pk_recip,
&info[..],
&mut ciphertext[..],
aad,
&mut csprng,
)
.expect("single_shot_seal() failed");
assert!(&ciphertext[..] != &msg[..]);
single_shot_open::<A, Kdf, Kem>(
&receiver_mode,
&sk_recip,
&encapped_key,
info,
&mut ciphertext[..],
aad,
&tag,
)
.expect("single_shot_open() failed");
let decrypted = ciphertext;
assert_eq!(&decrypted[..], &msg[..]);
}
};
}
#[cfg(feature = "x25519-dalek")]
test_single_shot_correctness!(
test_single_shot_correctness_x25519,
ChaCha20Poly1305,
HkdfSha256,
crate::kem::X25519HkdfSha256
);
#[cfg(feature = "p256")]
test_single_shot_correctness!(
test_single_shot_correctness_p256,
ChaCha20Poly1305,
HkdfSha256,
crate::kem::DhP256HkdfSha256
);
}