use std::marker::PhantomData;
use super::cipher::Cipher;
use crate::curve::Curve;
pub struct E;
pub struct S;
pub struct Ee;
pub struct Es;
pub struct Se;
pub struct Ss;
pub struct Psk;
pub struct Nil;
pub struct Cons<H, T>(PhantomData<fn() -> (H, T)>);
pub struct ToResponder;
pub struct ToInitiator;
pub struct Message<Dir, Tokens>(PhantomData<fn() -> (Dir, Tokens)>);
#[doc(hidden)]
pub trait TokenFlag {
const IS_PSK: bool;
}
impl TokenFlag for E {
const IS_PSK: bool = false;
}
impl TokenFlag for S {
const IS_PSK: bool = false;
}
impl TokenFlag for Ee {
const IS_PSK: bool = false;
}
impl TokenFlag for Es {
const IS_PSK: bool = false;
}
impl TokenFlag for Se {
const IS_PSK: bool = false;
}
impl TokenFlag for Ss {
const IS_PSK: bool = false;
}
impl TokenFlag for Psk {
const IS_PSK: bool = true;
}
#[doc(hidden)]
pub trait TokensContainPsk {
const VALUE: bool;
}
impl TokensContainPsk for Nil {
const VALUE: bool = false;
}
impl<Tok: TokenFlag, Rest: TokensContainPsk> TokensContainPsk for Cons<Tok, Rest> {
const VALUE: bool = Tok::IS_PSK || Rest::VALUE;
}
#[doc(hidden)]
pub trait ContainsPsk {
const VALUE: bool;
}
impl ContainsPsk for Nil {
const VALUE: bool = false;
}
impl<Dir, Toks: TokensContainPsk, Rest: ContainsPsk> ContainsPsk
for Cons<Message<Dir, Toks>, Rest>
{
const VALUE: bool = <Toks as TokensContainPsk>::VALUE || <Rest as ContainsPsk>::VALUE;
}
pub trait WireSize<Cu: Curve, Ci: Cipher, const HAS_PSK: bool> {
const SIZE_UNKEYED: usize;
const SIZE_KEYED: usize;
const KEYED_AFTER_UNKEYED: bool;
const KEYED_AFTER_KEYED: bool;
}
impl<Cu: Curve, Ci: Cipher> WireSize<Cu, Ci, false> for E {
const SIZE_UNKEYED: usize = Cu::PUBLIC_KEY_SIZE;
const SIZE_KEYED: usize = Cu::PUBLIC_KEY_SIZE;
const KEYED_AFTER_UNKEYED: bool = false;
const KEYED_AFTER_KEYED: bool = true;
}
impl<Cu: Curve, Ci: Cipher> WireSize<Cu, Ci, true> for E {
const SIZE_UNKEYED: usize = Cu::PUBLIC_KEY_SIZE;
const SIZE_KEYED: usize = Cu::PUBLIC_KEY_SIZE;
const KEYED_AFTER_UNKEYED: bool = true; const KEYED_AFTER_KEYED: bool = true;
}
impl<Cu: Curve, Ci: Cipher, const HAS_PSK: bool> WireSize<Cu, Ci, HAS_PSK> for S {
const SIZE_UNKEYED: usize = Cu::PUBLIC_KEY_SIZE;
const SIZE_KEYED: usize = Cu::PUBLIC_KEY_SIZE + Ci::TAG_SIZE;
const KEYED_AFTER_UNKEYED: bool = false;
const KEYED_AFTER_KEYED: bool = true;
}
impl<Cu: Curve, Ci: Cipher, const HAS_PSK: bool> WireSize<Cu, Ci, HAS_PSK> for Ee {
const SIZE_UNKEYED: usize = 0;
const SIZE_KEYED: usize = 0;
const KEYED_AFTER_UNKEYED: bool = true;
const KEYED_AFTER_KEYED: bool = true;
}
impl<Cu: Curve, Ci: Cipher, const HAS_PSK: bool> WireSize<Cu, Ci, HAS_PSK> for Es {
const SIZE_UNKEYED: usize = 0;
const SIZE_KEYED: usize = 0;
const KEYED_AFTER_UNKEYED: bool = true;
const KEYED_AFTER_KEYED: bool = true;
}
impl<Cu: Curve, Ci: Cipher, const HAS_PSK: bool> WireSize<Cu, Ci, HAS_PSK> for Se {
const SIZE_UNKEYED: usize = 0;
const SIZE_KEYED: usize = 0;
const KEYED_AFTER_UNKEYED: bool = true;
const KEYED_AFTER_KEYED: bool = true;
}
impl<Cu: Curve, Ci: Cipher, const HAS_PSK: bool> WireSize<Cu, Ci, HAS_PSK> for Ss {
const SIZE_UNKEYED: usize = 0;
const SIZE_KEYED: usize = 0;
const KEYED_AFTER_UNKEYED: bool = true;
const KEYED_AFTER_KEYED: bool = true;
}
impl<Cu: Curve, Ci: Cipher, const HAS_PSK: bool> WireSize<Cu, Ci, HAS_PSK> for Psk {
const SIZE_UNKEYED: usize = 0;
const SIZE_KEYED: usize = 0;
const KEYED_AFTER_UNKEYED: bool = true;
const KEYED_AFTER_KEYED: bool = true;
}
impl<Cu: Curve, Ci: Cipher, const HAS_PSK: bool> WireSize<Cu, Ci, HAS_PSK> for Nil {
const SIZE_UNKEYED: usize = 0;
const SIZE_KEYED: usize = 0;
const KEYED_AFTER_UNKEYED: bool = false;
const KEYED_AFTER_KEYED: bool = true;
}
impl<H, T, Cu: Curve, Ci: Cipher, const HAS_PSK: bool> WireSize<Cu, Ci, HAS_PSK> for Cons<H, T>
where
H: WireSize<Cu, Ci, HAS_PSK>,
T: WireSize<Cu, Ci, HAS_PSK>,
{
const SIZE_UNKEYED: usize = H::SIZE_UNKEYED
+ if H::KEYED_AFTER_UNKEYED {
T::SIZE_KEYED
} else {
T::SIZE_UNKEYED
};
const SIZE_KEYED: usize = H::SIZE_KEYED + T::SIZE_KEYED;
const KEYED_AFTER_UNKEYED: bool = if H::KEYED_AFTER_UNKEYED {
T::KEYED_AFTER_KEYED
} else {
T::KEYED_AFTER_UNKEYED
};
const KEYED_AFTER_KEYED: bool = T::KEYED_AFTER_KEYED;
}
#[macro_export]
macro_rules! noise_message_size {
(
curve: $Cu:ty,
cipher: $Ci:ty,
has_psk: $has_psk:tt,
keyed: $keyed:tt,
tokens: [$($tokens:tt),* $(,)?],
) => {
$crate::noise_message_size!(@accum
curve: $Cu,
cipher: $Ci,
has_psk: $has_psk,
keyed: $keyed,
remaining: [$($tokens),*],
size: 0,
)
};
(@accum
curve: $Cu:ty,
cipher: $Ci:ty,
has_psk: $_psk:tt,
keyed: true,
remaining: [],
size: $size:expr,
) => {
$size + <$Ci as $crate::noise::cipher::Cipher>::TAG_SIZE
};
(@accum
curve: $Cu:ty,
cipher: $Ci:ty,
has_psk: $_psk:tt,
keyed: false,
remaining: [],
size: $size:expr,
) => {
$size
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: true, keyed: $keyed:tt,
remaining: [E $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: true, keyed: true, remaining: [$($rest),*],
size: $size + <$Cu as $crate::curve::Curve>::PUBLIC_KEY_SIZE,
)
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: false, keyed: $keyed:tt,
remaining: [E $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: false, keyed: $keyed, remaining: [$($rest),*],
size: $size + <$Cu as $crate::curve::Curve>::PUBLIC_KEY_SIZE,
)
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: $psk:tt, keyed: true,
remaining: [S $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: $psk, keyed: true, remaining: [$($rest),*],
size: $size + <$Cu as $crate::curve::Curve>::PUBLIC_KEY_SIZE
+ <$Ci as $crate::noise::cipher::Cipher>::TAG_SIZE,
)
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: $psk:tt, keyed: false,
remaining: [S $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: $psk, keyed: false, remaining: [$($rest),*],
size: $size + <$Cu as $crate::curve::Curve>::PUBLIC_KEY_SIZE,
)
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: $psk:tt, keyed: $keyed:tt,
remaining: [Es $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: $psk, keyed: true, remaining: [$($rest),*], size: $size,
)
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: $psk:tt, keyed: $keyed:tt,
remaining: [Ee $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: $psk, keyed: true, remaining: [$($rest),*], size: $size,
)
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: $psk:tt, keyed: $keyed:tt,
remaining: [Se $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: $psk, keyed: true, remaining: [$($rest),*], size: $size,
)
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: $psk:tt, keyed: $keyed:tt,
remaining: [Ss $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: $psk, keyed: true, remaining: [$($rest),*], size: $size,
)
};
(@accum curve: $Cu:ty, cipher: $Ci:ty, has_psk: $psk:tt, keyed: $keyed:tt,
remaining: [Psk $(, $rest:tt)*], size: $size:expr,
) => {
$crate::noise_message_size!(@accum curve: $Cu, cipher: $Ci,
has_psk: $psk, keyed: true, remaining: [$($rest),*], size: $size,
)
};
}
#[cfg(test)]
#[allow(clippy::bool_assert_comparison)]
mod tests {
use super::*;
use crate::noise::cipher::ChaChaPoly;
use crate::noise::curve::P256;
type Msg1Tokens = Cons<E, Cons<Es, Cons<S, Cons<Ss, Cons<Psk, Nil>>>>>; type Msg2Tokens = Cons<E, Cons<Ee, Cons<Se, Nil>>>;
#[test]
fn ikpsk1_msg1_size() {
assert_eq!(
<Msg1Tokens as WireSize<P256, ChaChaPoly, true>>::SIZE_UNKEYED,
65 + 81 );
assert_eq!(
<Msg1Tokens as WireSize<P256, ChaChaPoly, true>>::KEYED_AFTER_UNKEYED,
true
);
}
#[test]
fn ikpsk1_msg2_size() {
assert_eq!(
<Msg2Tokens as WireSize<P256, ChaChaPoly, true>>::SIZE_KEYED,
65
);
assert_eq!(
<Msg2Tokens as WireSize<P256, ChaChaPoly, true>>::KEYED_AFTER_KEYED,
true
);
}
#[test]
fn non_psk_e_does_not_key() {
assert_eq!(
<E as WireSize<P256, ChaChaPoly, false>>::KEYED_AFTER_UNKEYED,
false
);
type EsThenS = Cons<E, Cons<S, Nil>>;
assert_eq!(
<EsThenS as WireSize<P256, ChaChaPoly, false>>::SIZE_UNKEYED,
65 + 65 );
}
#[test]
fn dh_before_s_encrypts_s() {
type EEsS = Cons<E, Cons<Es, Cons<S, Nil>>>;
assert_eq!(
<EEsS as WireSize<P256, ChaChaPoly, false>>::SIZE_UNKEYED,
65 + (65 + 16) );
}
#[test]
fn macro_ikpsk1_msg1() {
const SIZE: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: true,
keyed: false,
tokens: [E, Es, S, Ss, Psk],
);
assert_eq!(SIZE, 162);
}
#[test]
fn macro_ikpsk1_msg2() {
const SIZE: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: true,
keyed: true,
tokens: [E, Ee, Se],
);
assert_eq!(SIZE, 81);
}
#[test]
fn macro_n_msg1() {
const SIZE: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: false,
keyed: false,
tokens: [E, Es],
);
assert_eq!(SIZE, 81);
}
#[test]
fn macro_k_msg1() {
const SIZE: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: false,
keyed: false,
tokens: [E, Es, Ss],
);
assert_eq!(SIZE, 81);
}
#[test]
fn macro_kpsk0_msg1() {
const SIZE: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: true,
keyed: false,
tokens: [Psk, E, Es, Ss],
);
assert_eq!(SIZE, 81);
}
#[test]
fn macro_non_psk_e_s_unkeyed() {
const SIZE: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: false,
keyed: false,
tokens: [E, S],
);
assert_eq!(SIZE, 130);
}
#[test]
fn macro_dh_before_s_encrypts_s() {
const SIZE: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: false,
keyed: false,
tokens: [E, Es, S],
);
assert_eq!(SIZE, 162);
}
#[test]
fn macro_sizes_are_const() {
const MSG1: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: true,
keyed: false,
tokens: [E, Es, S, Ss, Psk],
);
const MSG2: usize = noise_message_size!(
curve: P256,
cipher: ChaChaPoly,
has_psk: true,
keyed: true,
tokens: [E, Ee, Se],
);
let _buf1: [u8; MSG1] = [0u8; 162];
let _buf2: [u8; MSG2] = [0u8; 81];
}
#[test]
fn macro_matches_wire_size_trait() {
let trait_msg1 =
<Msg1Tokens as WireSize<P256, ChaChaPoly, true>>::SIZE_UNKEYED + ChaChaPoly::TAG_SIZE; const MACRO_MSG1: usize = noise_message_size!(
curve: P256, cipher: ChaChaPoly, has_psk: true, keyed: false,
tokens: [E, Es, S, Ss, Psk],
);
assert_eq!(MACRO_MSG1, trait_msg1);
let trait_msg2 =
<Msg2Tokens as WireSize<P256, ChaChaPoly, true>>::SIZE_KEYED + ChaChaPoly::TAG_SIZE;
const MACRO_MSG2: usize = noise_message_size!(
curve: P256, cipher: ChaChaPoly, has_psk: true, keyed: true,
tokens: [E, Ee, Se],
);
assert_eq!(MACRO_MSG2, trait_msg2);
}
}