use crate::errors::UnknownCryptoError;
use crate::hazardous::hpke::suite::private::*;
use crate::hazardous::hpke::Role;
use private::*;
pub(crate) mod private {
use crate::errors::UnknownCryptoError;
pub trait Base {}
pub trait Psk {}
pub trait Auth {}
pub trait AuthPsk {}
#[repr(u8)]
pub enum HpkeMode {
Base = 0x00u8,
Psk = 0x01u8,
Auth = 0x02u8,
AuthPsk = 0x03u8,
}
impl HpkeMode {
pub(crate) fn verify_psk_inputs(
&self,
psk: &[u8],
psk_id: &[u8],
) -> Result<(), UnknownCryptoError> {
match *self {
HpkeMode::Base | HpkeMode::Auth => {
match (psk.is_empty(), psk_id.is_empty()) {
(true, true) => Ok(()),
(_, _) => Err(UnknownCryptoError), }
}
HpkeMode::Psk | HpkeMode::AuthPsk => {
match (psk.is_empty(), psk_id.is_empty()) {
(false, false) => Ok(()), (_, _) => Err(UnknownCryptoError), }
}
}
}
pub fn mode_id(&self) -> u8 {
match self {
Self::Base => 0x00u8,
Self::Psk => 0x01u8,
Self::Auth => 0x02u8,
Self::AuthPsk => 0x03u8,
}
}
}
}
#[cfg_attr(test, derive(Clone))]
#[derive(Debug, PartialEq)]
pub struct ModeBase<S> {
suite: S,
role: Role,
}
impl<S> ModeBase<S> {
pub const MODE_ID: u8 = 0x00u8;
}
impl<S: Suite + Base> ModeBase<S> {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
pub fn new_sender(
pubkey_r: &S::PublicKey,
info: &[u8],
) -> Result<(Self, S::EncapsulatedKey), UnknownCryptoError> {
let (suite, ek) = S::setup_base_sender(pubkey_r, info)?;
Ok((
(Self {
suite,
role: Role::Sender,
}),
ek,
))
}
pub fn new_sender_deterministic(
pubkey_r: &S::PublicKey,
info: &[u8],
secret_ephemeral: S::PrivateKey,
) -> Result<(Self, S::EncapsulatedKey), UnknownCryptoError> {
let (suite, ek) = S::setup_base_sender_deterministic(pubkey_r, info, secret_ephemeral)?;
Ok((
(Self {
suite,
role: Role::Sender,
}),
ek,
))
}
pub fn new_recipient(
enc: &S::EncapsulatedKey,
secret_key_r: &S::PrivateKey,
info: &[u8],
) -> Result<Self, UnknownCryptoError> {
Ok(Self {
suite: S::setup_base_recipient(enc, secret_key_r, info)?,
role: Role::Recipient,
})
}
pub fn seal(
&mut self,
plaintext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if self.role != Role::Sender {
return Err(UnknownCryptoError);
}
self.suite.seal(plaintext, aad, out)
}
pub fn open(
&mut self,
ciphertext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if self.role != Role::Recipient {
return Err(UnknownCryptoError);
}
self.suite.open(ciphertext, aad, out)
}
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
pub fn base_seal(
pubkey_r: &S::PublicKey,
info: &[u8],
plaintext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<S::EncapsulatedKey, UnknownCryptoError> {
let (mut ctx, ek) = Self::new_sender(pubkey_r, info)?;
ctx.seal(plaintext, aad, out)?;
Ok(ek)
}
pub fn base_open(
enc: &S::EncapsulatedKey,
secret_key_r: &S::PrivateKey,
info: &[u8],
ciphertext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let mut ctx = Self::new_recipient(enc, secret_key_r, info)?;
ctx.open(ciphertext, aad, out)
}
pub fn export_secret(
&self,
exporter_context: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
self.suite.export(exporter_context, out)
}
}
#[cfg_attr(test, derive(Clone))]
#[derive(Debug, PartialEq)]
pub struct ModePsk<S> {
suite: S,
role: Role,
}
impl<S> ModePsk<S> {
pub const MODE_ID: u8 = 0x01u8;
}
impl<S: Suite + Psk> ModePsk<S> {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
pub fn new_sender(
pubkey_r: &S::PublicKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
) -> Result<(Self, S::EncapsulatedKey), UnknownCryptoError> {
let (suite, ek) = S::setup_psk_sender(pubkey_r, info, psk, psk_id)?;
Ok((
(Self {
suite,
role: Role::Sender,
}),
ek,
))
}
pub fn new_sender_deterministic(
pubkey_r: &S::PublicKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
secret_ephemeral: S::PrivateKey,
) -> Result<(Self, S::EncapsulatedKey), UnknownCryptoError> {
let (suite, ek) =
S::setup_psk_sender_deterministic(pubkey_r, info, psk, psk_id, secret_ephemeral)?;
Ok((
(Self {
suite,
role: Role::Sender,
}),
ek,
))
}
pub fn new_recipient(
enc: &S::EncapsulatedKey,
secret_key_r: &S::PrivateKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
) -> Result<Self, UnknownCryptoError> {
Ok(Self {
suite: S::setup_psk_recipient(enc, secret_key_r, info, psk, psk_id)?,
role: Role::Recipient,
})
}
pub fn seal(
&mut self,
plaintext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if self.role != Role::Sender {
return Err(UnknownCryptoError);
}
self.suite.seal(plaintext, aad, out)
}
pub fn open(
&mut self,
ciphertext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if self.role != Role::Recipient {
return Err(UnknownCryptoError);
}
self.suite.open(ciphertext, aad, out)
}
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
pub fn psk_seal(
pubkey_r: &S::PublicKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
plaintext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<S::EncapsulatedKey, UnknownCryptoError> {
let (mut ctx, ek) = Self::new_sender(pubkey_r, info, psk, psk_id)?;
ctx.seal(plaintext, aad, out)?;
Ok(ek)
}
#[allow(clippy::too_many_arguments)]
pub fn psk_open(
enc: &S::EncapsulatedKey,
secret_key_r: &S::PrivateKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
ciphertext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let mut ctx = Self::new_recipient(enc, secret_key_r, info, psk, psk_id)?;
ctx.open(ciphertext, aad, out)
}
pub fn export_secret(
&self,
exporter_context: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
self.suite.export(exporter_context, out)
}
}
#[cfg_attr(test, derive(Clone))]
#[derive(Debug, PartialEq)]
pub struct ModeAuth<S> {
suite: S,
role: Role,
}
impl<S> ModeAuth<S> {
pub const MODE_ID: u8 = 0x02u8;
}
impl<S: Suite + Auth> ModeAuth<S> {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
pub fn new_sender(
pubkey_r: &S::PublicKey,
info: &[u8],
secret_key_s: &S::PrivateKey,
) -> Result<(Self, S::EncapsulatedKey), UnknownCryptoError> {
let (suite, ek) = S::setup_auth_sender(pubkey_r, info, secret_key_s)?;
Ok((
(Self {
suite,
role: Role::Sender,
}),
ek,
))
}
pub fn new_sender_deterministic(
pubkey_r: &S::PublicKey,
info: &[u8],
secret_key_s: &S::PrivateKey,
secret_ephemeral: S::PrivateKey,
) -> Result<(Self, S::EncapsulatedKey), UnknownCryptoError> {
let (suite, ek) =
S::setup_auth_sender_deterministic(pubkey_r, info, secret_key_s, secret_ephemeral)?;
Ok((
(Self {
suite,
role: Role::Sender,
}),
ek,
))
}
pub fn new_recipient(
enc: &S::EncapsulatedKey,
secret_key_r: &S::PrivateKey,
info: &[u8],
pubkey_s: &S::PublicKey,
) -> Result<Self, UnknownCryptoError> {
Ok(Self {
suite: S::setup_auth_recipient(enc, secret_key_r, info, pubkey_s)?,
role: Role::Recipient,
})
}
pub fn seal(
&mut self,
plaintext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if self.role != Role::Sender {
return Err(UnknownCryptoError);
}
self.suite.seal(plaintext, aad, out)
}
pub fn open(
&mut self,
ciphertext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if self.role != Role::Recipient {
return Err(UnknownCryptoError);
}
self.suite.open(ciphertext, aad, out)
}
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
pub fn auth_seal(
pubkey_r: &S::PublicKey,
info: &[u8],
secrety_key_s: &S::PrivateKey,
plaintext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<S::EncapsulatedKey, UnknownCryptoError> {
let (mut ctx, ek) = Self::new_sender(pubkey_r, info, secrety_key_s)?;
ctx.seal(plaintext, aad, out)?;
Ok(ek)
}
pub fn auth_open(
enc: &S::EncapsulatedKey,
secret_key_r: &S::PrivateKey,
info: &[u8],
pubkey_s: &S::PublicKey,
ciphertext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let mut ctx = Self::new_recipient(enc, secret_key_r, info, pubkey_s)?;
ctx.open(ciphertext, aad, out)
}
pub fn export_secret(
&self,
exporter_context: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
self.suite.export(exporter_context, out)
}
}
#[cfg_attr(test, derive(Clone))]
#[derive(Debug, PartialEq)]
pub struct ModeAuthPsk<S> {
suite: S,
role: Role,
}
impl<S> ModeAuthPsk<S> {
pub const MODE_ID: u8 = 0x03u8;
}
impl<S: Suite + AuthPsk> ModeAuthPsk<S> {
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
pub fn new_sender(
pubkey_r: &S::PublicKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
secret_key_s: &S::PrivateKey,
) -> Result<(Self, S::EncapsulatedKey), UnknownCryptoError> {
let (suite, ek) = S::setup_authpsk_sender(pubkey_r, info, psk, psk_id, secret_key_s)?;
Ok((
(Self {
suite,
role: Role::Sender,
}),
ek,
))
}
pub fn new_sender_deterministic(
pubkey_r: &S::PublicKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
secret_key_s: &S::PrivateKey,
secret_ephemeral: S::PrivateKey,
) -> Result<(Self, S::EncapsulatedKey), UnknownCryptoError> {
let (suite, ek) = S::setup_authpsk_sender_deterministic(
pubkey_r,
info,
psk,
psk_id,
secret_key_s,
secret_ephemeral,
)?;
Ok((
(Self {
suite,
role: Role::Sender,
}),
ek,
))
}
pub fn new_recipient(
enc: &S::EncapsulatedKey,
secret_key_r: &S::PrivateKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
pubkey_s: &S::PublicKey,
) -> Result<Self, UnknownCryptoError> {
Ok(Self {
suite: S::setup_authpsk_recipient(enc, secret_key_r, info, psk, psk_id, pubkey_s)?,
role: Role::Recipient,
})
}
pub fn seal(
&mut self,
plaintext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if self.role != Role::Sender {
return Err(UnknownCryptoError);
}
self.suite.seal(plaintext, aad, out)
}
pub fn open(
&mut self,
ciphertext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if self.role != Role::Recipient {
return Err(UnknownCryptoError);
}
self.suite.open(ciphertext, aad, out)
}
#[cfg(feature = "safe_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
#[allow(clippy::too_many_arguments)]
pub fn authpsk_seal(
pubkey_r: &S::PublicKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
secrety_key_s: &S::PrivateKey,
plaintext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<S::EncapsulatedKey, UnknownCryptoError> {
let (mut ctx, ek) = Self::new_sender(pubkey_r, info, psk, psk_id, secrety_key_s)?;
ctx.seal(plaintext, aad, out)?;
Ok(ek)
}
#[allow(clippy::too_many_arguments)]
pub fn authpsk_open(
enc: &S::EncapsulatedKey,
secret_key_r: &S::PrivateKey,
info: &[u8],
psk: &[u8],
psk_id: &[u8],
pubkey_s: &S::PublicKey,
ciphertext: &[u8],
aad: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
let mut ctx = Self::new_recipient(enc, secret_key_r, info, psk, psk_id, pubkey_s)?;
ctx.open(ciphertext, aad, out)
}
pub fn export_secret(
&self,
exporter_context: &[u8],
out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
self.suite.export(exporter_context, out)
}
}
#[cfg(test)]
#[cfg(feature = "safe_api")]
mod test {
use super::*;
use crate::hazardous::{hpke::DHKEM_X25519_SHA256_CHACHA20, kem::x25519_hkdf_sha256::DhKem};
#[test]
fn test_internal_hpke_mode_verifier() {
assert!(HpkeMode::Base.verify_psk_inputs(b"", b"").is_ok());
assert!(HpkeMode::Base.verify_psk_inputs(b"", b"a").is_err());
assert!(HpkeMode::Base.verify_psk_inputs(b"a", b"").is_err());
assert!(HpkeMode::Base.verify_psk_inputs(b"a", b"a").is_err());
assert!(HpkeMode::Auth.verify_psk_inputs(b"", b"").is_ok());
assert!(HpkeMode::Auth.verify_psk_inputs(b"", b"a").is_err());
assert!(HpkeMode::Auth.verify_psk_inputs(b"a", b"").is_err());
assert!(HpkeMode::Auth.verify_psk_inputs(b"a", b"a").is_err());
assert!(HpkeMode::Psk.verify_psk_inputs(b"", b"").is_err());
assert!(HpkeMode::Psk.verify_psk_inputs(b"", b"a").is_err());
assert!(HpkeMode::Psk.verify_psk_inputs(b"a", b"").is_err());
assert!(HpkeMode::Psk.verify_psk_inputs(b"a", b"a").is_ok());
assert!(HpkeMode::AuthPsk.verify_psk_inputs(b"", b"").is_err());
assert!(HpkeMode::AuthPsk.verify_psk_inputs(b"", b"a").is_err());
assert!(HpkeMode::AuthPsk.verify_psk_inputs(b"a", b"").is_err());
assert!(HpkeMode::AuthPsk.verify_psk_inputs(b"a", b"a").is_ok());
}
#[test]
fn test_error_on_mismatched_role() {
let (sk_s, pk_s) = DhKem::derive_keypair(&[0u8; 64]).unwrap();
let (sk_r, pk_r) = DhKem::derive_keypair(&[255u8; 64]).unwrap();
let mut pt = [1u8; 32];
let mut out_ct = [0u8; 32 + 16];
let (mut ctx_s, enc) =
ModeBase::<DHKEM_X25519_SHA256_CHACHA20>::new_sender(&pk_r, &[0u8; 64]).unwrap();
let mut ctx_r =
ModeBase::<DHKEM_X25519_SHA256_CHACHA20>::new_recipient(&enc, &sk_r, &[0u8; 64])
.unwrap();
assert!(ctx_r.seal(&pt, b"", &mut out_ct).is_err());
ctx_s.seal(&pt, b"", &mut out_ct).unwrap();
assert!(ctx_s.open(&out_ct, b"", &mut pt).is_err());
ctx_r.open(&out_ct, b"", &mut pt).unwrap();
assert_eq!(&pt, &[1u8; 32]);
let mut export_s = [0u8; 64];
let mut export_r = [0u8; 64];
ctx_s.export_secret(b"some context", &mut export_s).unwrap();
ctx_r.export_secret(b"some context", &mut export_r).unwrap();
assert_eq!(export_s, export_r);
let (mut ctx_s, enc) = ModePsk::<DHKEM_X25519_SHA256_CHACHA20>::new_sender(
&pk_r, &[0u8; 64], &[1u8; 32], b"psk_id",
)
.unwrap();
let mut ctx_r = ModePsk::<DHKEM_X25519_SHA256_CHACHA20>::new_recipient(
&enc, &sk_r, &[0u8; 64], &[1u8; 32], b"psk_id",
)
.unwrap();
assert!(ctx_r.seal(&pt, b"", &mut out_ct).is_err());
ctx_s.seal(&pt, b"", &mut out_ct).unwrap();
assert!(ctx_s.open(&out_ct, b"", &mut pt).is_err());
ctx_r.open(&out_ct, b"", &mut pt).unwrap();
assert_eq!(&pt, &[1u8; 32]);
let mut export_s = [0u8; 64];
let mut export_r = [0u8; 64];
ctx_s.export_secret(b"some context", &mut export_s).unwrap();
ctx_r.export_secret(b"some context", &mut export_r).unwrap();
assert_eq!(export_s, export_r);
let (mut ctx_s, enc) =
ModeAuth::<DHKEM_X25519_SHA256_CHACHA20>::new_sender(&pk_r, &[0u8; 64], &sk_s).unwrap();
let mut ctx_r =
ModeAuth::<DHKEM_X25519_SHA256_CHACHA20>::new_recipient(&enc, &sk_r, &[0u8; 64], &pk_s)
.unwrap();
assert!(ctx_r.seal(&pt, b"", &mut out_ct).is_err());
ctx_s.seal(&pt, b"", &mut out_ct).unwrap();
assert!(ctx_s.open(&out_ct, b"", &mut pt).is_err());
ctx_r.open(&out_ct, b"", &mut pt).unwrap();
assert_eq!(&pt, &[1u8; 32]);
let mut export_s = [0u8; 64];
let mut export_r = [0u8; 64];
ctx_s.export_secret(b"some context", &mut export_s).unwrap();
ctx_r.export_secret(b"some context", &mut export_r).unwrap();
assert_eq!(export_s, export_r);
let (mut ctx_s, enc) = ModeAuthPsk::<DHKEM_X25519_SHA256_CHACHA20>::new_sender(
&pk_r, &[0u8; 64], &[1u8; 32], b"psk_id", &sk_s,
)
.unwrap();
let mut ctx_r = ModeAuthPsk::<DHKEM_X25519_SHA256_CHACHA20>::new_recipient(
&enc, &sk_r, &[0u8; 64], &[1u8; 32], b"psk_id", &pk_s,
)
.unwrap();
assert!(ctx_r.seal(&pt, b"", &mut out_ct).is_err());
ctx_s.seal(&pt, b"", &mut out_ct).unwrap();
assert!(ctx_s.open(&out_ct, b"", &mut pt).is_err());
ctx_r.open(&out_ct, b"", &mut pt).unwrap();
assert_eq!(&pt, &[1u8; 32]);
let mut export_s = [0u8; 64];
let mut export_r = [0u8; 64];
ctx_s.export_secret(b"some context", &mut export_s).unwrap();
ctx_r.export_secret(b"some context", &mut export_r).unwrap();
assert_eq!(export_s, export_r);
}
}