use crate::{
HpkeError,
aead::{Aead, AeadTag},
kdf::Kdf as KdfTrait,
kem::Kem as KemTrait,
op_mode::{OpModeR, OpModeS},
setup::{setup_receiver, setup_sender_with_rng},
};
use aead::inout::InOutBuf;
#[cfg(feature = "getrandom")]
use getrandom::SysRng;
use rand_core::CryptoRng;
#[cfg(feature = "getrandom")]
use rand_core::UnwrapErr;
#[cfg(feature = "getrandom")]
pub fn single_shot_seal_inout_detached<A, Kdf, Kem>(
mode: &OpModeS<Kem>,
pk_recip: &Kem::PublicKey,
info: &[u8],
buffer: InOutBuf<'_, '_, u8>,
aad: &[u8],
) -> Result<(Kem::EncappedKey, AeadTag<A>), HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
{
single_shot_seal_inout_detached_with_rng::<A, Kdf, Kem>(
mode,
pk_recip,
info,
buffer,
aad,
&mut UnwrapErr(SysRng),
)
}
pub fn single_shot_seal_inout_detached_with_rng<A, Kdf, Kem>(
mode: &OpModeS<Kem>,
pk_recip: &Kem::PublicKey,
info: &[u8],
buffer: InOutBuf<'_, '_, u8>,
aad: &[u8],
csprng: &mut impl CryptoRng,
) -> Result<(Kem::EncappedKey, AeadTag<A>), HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
{
let (encapped_key, mut aead_ctx) =
setup_sender_with_rng::<A, Kdf, Kem>(mode, pk_recip, info, csprng)?;
let tag = aead_ctx.seal_inout_detached(buffer, aad)?;
Ok((encapped_key, tag))
}
#[cfg(all(feature = "alloc", feature = "getrandom"))]
pub fn single_shot_seal<A, Kdf, Kem>(
mode: &OpModeS<Kem>,
pk_recip: &Kem::PublicKey,
info: &[u8],
plaintext: &[u8],
aad: &[u8],
) -> Result<(Kem::EncappedKey, crate::Vec<u8>), HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
{
single_shot_seal_with_rng::<A, Kdf, Kem>(
mode,
pk_recip,
info,
plaintext,
aad,
&mut UnwrapErr(SysRng),
)
}
#[cfg(feature = "alloc")]
pub fn single_shot_seal_with_rng<A, Kdf, Kem>(
mode: &OpModeS<Kem>,
pk_recip: &Kem::PublicKey,
info: &[u8],
plaintext: &[u8],
aad: &[u8],
csprng: &mut impl CryptoRng,
) -> Result<(Kem::EncappedKey, crate::Vec<u8>), HpkeError>
where
A: Aead,
Kdf: KdfTrait,
Kem: KemTrait,
{
let (encapped_key, mut aead_ctx) =
setup_sender_with_rng::<A, Kdf, Kem>(mode, pk_recip, info, csprng)?;
let ciphertext = aead_ctx.seal(plaintext, aad)?;
Ok((encapped_key, ciphertext))
}
pub fn single_shot_open_inout_detached<A, Kdf, Kem>(
mode: &OpModeR<Kem>,
sk_recip: &Kem::PrivateKey,
encapped_key: &Kem::EncappedKey,
info: &[u8],
buffer: InOutBuf<'_, '_, 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_inout_detached(buffer, aad, tag)
}
#[cfg(feature = "alloc")]
pub fn single_shot_open<A, Kdf, Kem>(
mode: &OpModeR<Kem>,
sk_recip: &Kem::PrivateKey,
encapped_key: &Kem::EncappedKey,
info: &[u8],
ciphertext: &[u8],
aad: &[u8],
) -> Result<crate::Vec<u8>, 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)
}
#[cfg(feature = "alloc")]
#[cfg(test)]
mod test {
use super::*;
use crate::{
kdf::*,
kem::{Kem as KemTrait, *},
op_mode::{OpModeR, OpModeS, PskBundle},
test_util::gen_rand_buf,
};
#[cfg(feature = "chacha")]
use crate::aead::ChaCha20Poly1305;
macro_rules! test_single_shot_correctness {
($test_name:ident, $aead:ty, $kdf:ty, $kem:ty, $use_auth:expr) => {
#[test]
fn $test_name() {
type A = $aead;
type Kdf = $kdf;
type Kem = $kem;
let msg = b"Good night, a-ding ding ding ding ding";
let aad = b"Five four three two one";
let mut csprng = rand::rng();
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::new(&psk, &psk_id).unwrap();
let (sk_sender_id, pk_sender_id) = Kem::gen_keypair_with_rng(&mut csprng);
let (sk_recip, pk_recip) = Kem::gen_keypair_with_rng(&mut csprng);
let sender_mode = if $use_auth {
OpModeS::<Kem>::AuthPsk(
(sk_sender_id, pk_sender_id.clone()),
psk_bundle.clone(),
)
} else {
OpModeS::<Kem>::Psk(psk_bundle.clone())
};
let receiver_mode = if $use_auth {
OpModeR::<Kem>::AuthPsk(pk_sender_id, psk_bundle)
} else {
OpModeR::<Kem>::Psk(psk_bundle)
};
let (encapped_key, ciphertext) = single_shot_seal_with_rng::<A, Kdf, Kem>(
&sender_mode,
&pk_recip,
info,
msg,
aad,
&mut csprng,
)
.expect("single_shot_seal() failed");
assert!(&ciphertext[..] != &msg[..]);
let decrypted = single_shot_open::<A, Kdf, Kem>(
&receiver_mode,
&sk_recip,
&encapped_key,
info,
&ciphertext,
aad,
)
.expect("single_shot_open() failed");
assert_eq!(&decrypted, &msg);
}
};
}
#[cfg(all(feature = "x25519", feature = "chacha"))]
mod x25519_tests {
use super::*;
test_single_shot_correctness!(
test_single_shot_correctness_x25519,
ChaCha20Poly1305,
HkdfSha256,
X25519HkdfSha256,
true
);
}
#[cfg(all(feature = "nistp", feature = "chacha"))]
mod nistp_tests {
use super::*;
test_single_shot_correctness!(
test_single_shot_correctness_p256,
ChaCha20Poly1305,
HkdfSha256,
DhP256HkdfSha256,
true
);
test_single_shot_correctness!(
test_single_shot_correctness_p384,
ChaCha20Poly1305,
HkdfSha384,
DhP384HkdfSha384,
true
);
test_single_shot_correctness!(
test_single_shot_correctness_p521,
ChaCha20Poly1305,
HkdfSha512,
DhP521HkdfSha512,
true
);
}
#[cfg(all(feature = "mlkem", feature = "chacha"))]
mod mlkem_tests {
use super::*;
test_single_shot_correctness!(
test_single_shot_correctness_mlkem768,
ChaCha20Poly1305,
KdfShake128,
MlKem768,
false
);
test_single_shot_correctness!(
test_single_shot_correctness_mlkem1024,
ChaCha20Poly1305,
KdfShake256,
MlKem1024,
false
);
}
#[cfg(all(feature = "mlkem", feature = "nistp", feature = "chacha"))]
mod mlkem_nistp_tests {
use super::*;
test_single_shot_correctness!(
test_single_shot_correctness_mlkem768p256,
ChaCha20Poly1305,
KdfShake128,
MlKem768P256,
false
);
test_single_shot_correctness!(
test_single_shot_correctness_mlkem1024p384,
ChaCha20Poly1305,
KdfShake256,
MlKem1024P384,
false
);
}
#[cfg(all(feature = "mlkem", feature = "x25519", feature = "chacha"))]
mod xwing_tests {
use super::*;
test_single_shot_correctness!(
test_single_shot_correctness_xwing,
ChaCha20Poly1305,
KdfTurboShake128,
XWing,
false
);
}
}