use hiss::noise::{Blake2b, ChaChaPoly, P256};
use crate::constants;
pub trait Channel {
type Curve: hiss::curve::DhCurve<PublicKey: AsRef<[u8]>>;
type Cipher: hiss::noise::Cipher;
type Hash: hiss::noise::Hash;
const PROTOCOL_NAME: &'static str;
const STATIC_PUBLIC_LEN: usize;
const AEAD_TAG_LEN: usize;
const MSG1_LEN: usize;
const MSG2_LEN: usize;
const INIT_PACKET_LEN: usize;
const RESP_PACKET_LEN: usize;
}
#[doc(hidden)]
pub const PROTOCOL_NAME_CAP: usize = 96;
#[doc(hidden)]
pub const fn protocol_name(
pattern: &str,
curve: &str,
cipher: &str,
hash: &str,
) -> ([u8; PROTOCOL_NAME_CAP], usize) {
let mut out = [0u8; PROTOCOL_NAME_CAP];
let mut len = 0;
len = append(&mut out, len, b"Noise_");
len = append(&mut out, len, pattern.as_bytes());
len = append(&mut out, len, b"_");
len = append(&mut out, len, curve.as_bytes());
len = append(&mut out, len, b"_");
len = append(&mut out, len, cipher.as_bytes());
len = append(&mut out, len, b"_");
len = append(&mut out, len, hash.as_bytes());
(out, len)
}
const fn append(out: &mut [u8; PROTOCOL_NAME_CAP], mut len: usize, src: &[u8]) -> usize {
assert!(
len + src.len() <= PROTOCOL_NAME_CAP,
"the suite's Noise protocol name exceeds PROTOCOL_NAME_CAP"
);
let mut i = 0;
while i < src.len() {
out[len] = src[i];
i += 1;
len += 1;
}
len
}
#[macro_export]
macro_rules! channel {
(
$(#[$meta:meta])*
$vis:vis $name:ident < $curve:ty, $cipher:ty, $hash:ty > ;
) => {
::hiss::noise! {
$vis IK<$curve, $cipher, $hash> {
<- s
...
-> e, es, s, ss [12]
<- e, ee, se
}
}
$crate::__channel_impl! {
$(#[$meta])*
$vis $name < $curve, $cipher, $hash >;
kind = no_psk;
psk_ty = ();
machine = IK;
initiator = IKInitiatorMsg1;
initiator_sent= IKInitiatorMsg2;
responder = IKResponderMsg1;
intro = IKResponderMsg1Intro;
responder_read= IKResponderMsg2;
}
};
}
#[macro_export]
macro_rules! channel_psk {
(
$(#[$meta:meta])*
$vis:vis $name:ident < $curve:ty, $cipher:ty, $hash:ty > ;
) => {
::hiss::noise! {
$vis IKpsk1<$curve, $cipher, $hash> {
<- s
...
-> e, es, s, ss, psk [12]
<- e, ee, se
}
}
$crate::__channel_impl! {
$(#[$meta])*
$vis $name < $curve, $cipher, $hash >;
kind = psk;
psk_ty = ::hiss::psk::Psk;
machine = IKpsk1;
initiator = IKpsk1InitiatorMsg1;
initiator_sent= IKpsk1InitiatorMsg2;
responder = IKpsk1ResponderMsg1;
intro = IKpsk1ResponderMsg1Intro;
responder_read= IKpsk1ResponderMsg2;
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __channel_impl {
(
$(#[$meta:meta])*
$vis:vis $name:ident < $curve:ty, $cipher:ty, $hash:ty >;
kind = $kind:ident;
psk_ty = $psk_ty:ty;
machine = $mach:ident;
initiator = $init:ident;
initiator_sent= $init_sent:ident;
responder = $resp:ident;
intro = $intro:ident;
responder_read= $resp_read:ident;
) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
$vis struct $name;
impl $crate::packet::Channel for $name {
type Curve = $curve;
type Cipher = $cipher;
type Hash = $hash;
const PROTOCOL_NAME: &'static str = {
const RAW: ([u8; $crate::packet::suite::PROTOCOL_NAME_CAP], usize) =
$crate::packet::suite::protocol_name(
<$mach as ::hiss::noise::Pattern>::NAME,
<$curve as ::hiss::curve::Curve>::NAME,
<$cipher as ::hiss::noise::Cipher>::NAME,
<$hash as ::hiss::noise::Hash>::NAME,
);
const BYTES: &[u8] = &RAW.0;
match ::core::str::from_utf8(BYTES.split_at(RAW.1).0) {
::core::result::Result::Ok(name) => name,
::core::result::Result::Err(_) => {
::core::panic!("the suite's Noise protocol name is not UTF-8")
}
}
};
const STATIC_PUBLIC_LEN: usize =
<$curve as ::hiss::curve::Curve>::PUBLIC_KEY_SIZE;
const AEAD_TAG_LEN: usize =
<$cipher as ::hiss::noise::Cipher>::TAG_SIZE;
const MSG1_LEN: usize = Self::STATIC_PUBLIC_LEN
+ (Self::STATIC_PUBLIC_LEN + Self::AEAD_TAG_LEN)
+ ($crate::constants::MSG1_PAYLOAD_LEN + Self::AEAD_TAG_LEN);
const MSG2_LEN: usize = Self::STATIC_PUBLIC_LEN + Self::AEAD_TAG_LEN;
const INIT_PACKET_LEN: usize = $crate::constants::INIT_HEADER_LEN
+ Self::MSG1_LEN
+ $crate::constants::MAC1_LEN;
const RESP_PACKET_LEN: usize = $crate::constants::RESP_HEADER_LEN
+ Self::MSG2_LEN
+ $crate::constants::MAC1_LEN;
}
impl $crate::packet::Handshake for $name {
type Initiator<P: ::hiss::provider::DhProvider<$curve>> = $init<P>;
type InitiatorSent<P: ::hiss::provider::DhProvider<$curve>> = $init_sent<P>;
type Responder<P: ::hiss::provider::DhProvider<$curve>> = $resp<P>;
type Msg1Intro<P: ::hiss::provider::DhProvider<$curve>> = $intro<P>;
type ResponderRead<P: ::hiss::provider::DhProvider<$curve>> = $resp_read<P>;
type Psk = $psk_ty;
type Transport = ::hiss::noise::Transport<$mach>;
type Seal = ::hiss::noise::DatagramSend<$mach>;
type Open = ::hiss::noise::DatagramRecv<$mach>;
fn initiator<P: ::hiss::provider::DhProvider<$curve>>(
provider: P,
prologue: &[u8],
remote_static: <$curve as ::hiss::curve::Curve>::PublicKey,
) -> $init<P> {
$mach::initiator(provider, prologue, remote_static)
}
fn write_msg1<P: ::hiss::provider::DhProvider<$curve>>(
state: $init<P>,
static_key: <P as ::hiss::provider::CryptoKeyProvider<$curve>>::PrivateKey,
psk: &Self::Psk,
payload: &[u8; $crate::constants::MSG1_PAYLOAD_LEN],
) -> ::core::result::Result<
(::std::vec::Vec<u8>, $init_sent<P>),
::hiss::noise::HandshakeError,
> {
let (bytes, next) =
$crate::__channel_write_msg1!($kind, state, static_key, psk, payload)?;
::core::result::Result::Ok((bytes.to_vec(), next))
}
fn read_msg2<P: ::hiss::provider::DhProvider<$curve>>(
state: $init_sent<P>,
msg2: &[u8],
) -> ::core::result::Result<
::hiss::noise::Transport<$mach>,
::hiss::noise::HandshakeError,
> {
let exact: &[u8; $mach::MSG2_SIZE] = match ::core::convert::TryFrom::try_from(msg2) {
::core::result::Result::Ok(exact) => exact,
::core::result::Result::Err(_) => {
return ::core::result::Result::Err(
::hiss::noise::HandshakeError::MessageTooShort,
);
}
};
state.read_message_2(exact)
}
fn responder<P: ::hiss::provider::DhProvider<$curve>>(
provider: P,
prologue: &[u8],
static_key: <P as ::hiss::provider::CryptoKeyProvider<$curve>>::PrivateKey,
) -> ::core::result::Result<$resp<P>, ::hiss::noise::HandshakeError> {
$mach::responder(provider, prologue, static_key)
}
fn read_msg1_intro<P: ::hiss::provider::DhProvider<$curve>>(
state: $resp<P>,
msg1: &[u8],
) -> ::core::result::Result<
(
<$curve as ::hiss::curve::Curve>::PublicKey,
$intro<P>,
),
::hiss::noise::HandshakeError,
> {
let exact: &[u8; $mach::MSG1_SIZE] = match ::core::convert::TryFrom::try_from(msg1) {
::core::result::Result::Ok(exact) => exact,
::core::result::Result::Err(_) => {
return ::core::result::Result::Err(
::hiss::noise::HandshakeError::MessageTooShort,
);
}
};
state.read_message_1_intro(exact)
}
fn complete<P: ::hiss::provider::DhProvider<$curve>>(
mid: $intro<P>,
psk: &Self::Psk,
) -> ::core::result::Result<
(
[u8; $crate::constants::MSG1_PAYLOAD_LEN],
$resp_read<P>,
),
::hiss::noise::HandshakeError,
> {
$crate::__channel_complete!($kind, mid, psk)
}
fn write_msg2<P: ::hiss::provider::DhProvider<$curve>>(
state: $resp_read<P>,
) -> ::core::result::Result<
(::std::vec::Vec<u8>, ::hiss::noise::Transport<$mach>),
::hiss::noise::HandshakeError,
> {
let (bytes, transport) = state.write_message_2()?;
::core::result::Result::Ok((bytes.to_vec(), transport))
}
fn into_datagram(
transport: ::hiss::noise::Transport<$mach>,
epoch_size: ::core::num::NonZeroU64,
) -> (
::hiss::noise::DatagramSend<$mach>,
::hiss::noise::DatagramRecv<$mach>,
) {
transport.into_datagram_with_epoch(epoch_size)
}
fn next_counter(seal: &::hiss::noise::DatagramSend<$mach>) -> u64 {
seal.next_counter()
}
fn session_id(
seal: &::hiss::noise::DatagramSend<$mach>,
) -> &::hiss::noise::SessionId {
seal.session_id()
}
fn seal(
seal: &mut ::hiss::noise::DatagramSend<$mach>,
ad: &[u8],
plaintext: &[u8],
out: &mut [u8],
) -> ::core::result::Result<(u64, usize), ::hiss::noise::HandshakeError> {
seal.encrypt_next(ad, plaintext, out)
}
fn open(
open: &mut ::hiss::noise::DatagramRecv<$mach>,
counter: u64,
ad: &[u8],
ciphertext: &[u8],
out: &mut [u8],
) -> ::core::result::Result<usize, ::hiss::noise::HandshakeError> {
open.decrypt_at(counter, ad, ciphertext, out)
}
}
const _: () = assert!(
<$name as $crate::packet::Channel>::MSG1_LEN == $mach::MSG1_SIZE,
"slither's §2.3 MSG1_LEN disagrees with hiss's computed MSG1_SIZE"
);
const _: () = assert!(
<$name as $crate::packet::Channel>::MSG2_LEN == $mach::MSG2_SIZE,
"slither's §2.3 MSG2_LEN disagrees with hiss's computed MSG2_SIZE"
);
const _: () = assert!(
<$name as $crate::packet::Channel>::INIT_PACKET_LEN
<= $crate::constants::MAX_DATAGRAM
);
const _: () = assert!(
<$name as $crate::packet::Channel>::RESP_PACKET_LEN
<= $crate::constants::MAX_DATAGRAM
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __channel_write_msg1 {
(no_psk, $state:expr, $key:expr, $psk:expr, $payload:expr) => {{
let _: &() = $psk;
$state.write_message_1($key, $payload)
}};
(psk, $state:expr, $key:expr, $psk:expr, $payload:expr) => {
$state.write_message_1($key, $psk, $payload)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __channel_complete {
(no_psk, $mid:expr, $psk:expr) => {{
let _: &() = $psk;
$mid.complete()
}};
(psk, $mid:expr, $psk:expr) => {
$mid.complete($psk)
};
}
crate::channel! {
pub ReferenceSuite<P256, ChaChaPoly, Blake2b>;
}
const _: () =
assert!(<ReferenceSuite as Channel>::STATIC_PUBLIC_LEN == constants::STATIC_PUBLIC_LEN);
const _: () = assert!(<ReferenceSuite as Channel>::AEAD_TAG_LEN == constants::AEAD_TAG_LEN);
const _: () = assert!(<ReferenceSuite as Channel>::MSG1_LEN == constants::IK_MSG1_LEN);
const _: () = assert!(<ReferenceSuite as Channel>::MSG2_LEN == constants::IK_MSG2_LEN);
const _: () = assert!(<ReferenceSuite as Channel>::INIT_PACKET_LEN == constants::INIT_PACKET_LEN);
const _: () = assert!(<ReferenceSuite as Channel>::RESP_PACKET_LEN == constants::RESP_PACKET_LEN);