use std::marker::PhantomData;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use super::WellFormed;
use super::buffers::{RecvBuffer, SendBuffer};
use super::cipher::Cipher;
use super::error::HandshakeError;
use super::handshake::HandshakeInner;
use super::hash::Hash;
use super::pattern::Pattern;
use super::process::{
do_ee, do_es_initiator, do_es_responder, do_psk, do_se_initiator, do_se_responder, do_ss,
recv_e, recv_payload, recv_s, recv_to_transport, send_e, send_payload, send_s,
};
use super::role::{Initiator, Responder, Role};
use super::tokens::*;
use super::transport::Transport;
use super::{Noise, Protocol};
use crate::curve::{Curve, DhCurve};
use crate::provider::{CryptoKeyProvider, DhProviderAsync};
const TOKEN_SCRATCH: usize = 128;
async fn async_stream_e<Cu, Ci, H, CP, Io>(
inner: &mut HandshakeInner<Cu, Ci, H, CP>,
stream: &mut Io,
) -> Result<(), HandshakeError>
where
Cu: DhCurve,
Cu::PublicKey: AsRef<[u8]>,
Ci: Cipher,
H: Hash,
CP: DhProviderAsync<Cu>,
Io: AsyncWrite + Unpin,
{
let mut scratch = [0u8; TOKEN_SCRATCH];
let mut buffer = SendBuffer::new(&mut scratch);
send_e(inner, &mut buffer).await?;
stream.write_all(buffer.finish()).await?;
Ok(())
}
async fn async_stream_s<Cu, Ci, H, CP, Io>(
inner: &mut HandshakeInner<Cu, Ci, H, CP>,
stream: &mut Io,
static_key: CP::PrivateKey,
) -> Result<(), HandshakeError>
where
Cu: Curve,
Cu::PublicKey: AsRef<[u8]>,
Ci: Cipher,
H: Hash,
CP: CryptoKeyProvider<Cu>,
Io: AsyncWrite + Unpin,
{
let mut scratch = [0u8; TOKEN_SCRATCH];
let mut buffer = SendBuffer::new(&mut scratch);
send_s(inner, &mut buffer, static_key)?;
stream.write_all(buffer.finish()).await?;
Ok(())
}
async fn async_read_e<Cu, Ci, H, CP, Io>(
inner: &mut HandshakeInner<Cu, Ci, H, CP>,
stream: &mut Io,
) -> Result<Cu::PublicKey, HandshakeError>
where
Cu: Curve,
Cu::PublicKey: AsRef<[u8]>,
Ci: Cipher,
H: Hash,
CP: CryptoKeyProvider<Cu>,
Io: AsyncRead + Unpin,
{
let pk_size = Cu::PUBLIC_KEY_SIZE;
let mut scratch = [0u8; TOKEN_SCRATCH];
stream.read_exact(&mut scratch[..pk_size]).await?;
let mut buffer = RecvBuffer::new(&scratch[..pk_size]);
recv_e(inner, &mut buffer)
}
async fn async_read_s<Cu, Ci, H, CP, Io>(
inner: &mut HandshakeInner<Cu, Ci, H, CP>,
stream: &mut Io,
) -> Result<Cu::PublicKey, HandshakeError>
where
Cu: Curve,
Cu::PublicKey: AsRef<[u8]>,
Ci: Cipher,
H: Hash,
CP: CryptoKeyProvider<Cu>,
Io: AsyncRead + Unpin,
{
let wire_len = if inner.symmetric.has_key() {
Cu::PUBLIC_KEY_SIZE + Ci::TAG_SIZE
} else {
Cu::PUBLIC_KEY_SIZE
};
const {
assert!(
Cu::PUBLIC_KEY_SIZE + Ci::TAG_SIZE <= TOKEN_SCRATCH,
"curve public key + AEAD tag exceeds the 128-byte scratch buffer"
)
};
let mut scratch = [0u8; TOKEN_SCRATCH];
stream.read_exact(&mut scratch[..wire_len]).await?;
let mut buffer = RecvBuffer::new(&scratch[..wire_len]);
recv_s(inner, &mut buffer)
}
async fn send_message_tail_async<Cu, Ci, H, CP, Io>(
inner: &mut HandshakeInner<Cu, Ci, H, CP>,
stream: &mut Io,
) -> Result<(), HandshakeError>
where
Cu: Curve,
Ci: Cipher,
H: Hash,
CP: CryptoKeyProvider<Cu>,
Io: AsyncWrite + Unpin,
{
let tag_len = if inner.symmetric.has_key() {
Ci::TAG_SIZE
} else {
0
};
let mut scratch = [0u8; TOKEN_SCRATCH];
let mut buffer = SendBuffer::new(&mut scratch[..tag_len]);
send_payload(inner, &mut buffer)?;
stream.write_all(buffer.finish()).await?;
stream.flush().await?;
Ok(())
}
async fn recv_message_tail_async<Cu, Ci, H, CP, Io>(
inner: &mut HandshakeInner<Cu, Ci, H, CP>,
stream: &mut Io,
) -> Result<(), HandshakeError>
where
Cu: Curve,
Ci: Cipher,
H: Hash,
CP: CryptoKeyProvider<Cu>,
Io: AsyncRead + Unpin,
{
let tag_len = if inner.symmetric.has_key() {
Ci::TAG_SIZE
} else {
0
};
let mut scratch = [0u8; TOKEN_SCRATCH];
stream.read_exact(&mut scratch[..tag_len]).await?;
let mut buffer = RecvBuffer::new(&scratch[..tag_len]);
recv_payload(inner, &mut buffer)
}
pub struct AsyncHandshake<Proto, R, Stage, Msgs, CP, Io>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
{
inner: HandshakeInner<Proto::Curve, Proto::Cipher, Proto::Hash, CP>,
stream: Io,
_marker: PhantomData<fn() -> (Proto, R, Stage, Msgs)>,
}
pub struct AsyncSending<Proto, R, Tokens, MsgRest, CP, Io>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
{
inner: HandshakeInner<Proto::Curve, Proto::Cipher, Proto::Hash, CP>,
stream: Io,
_marker: PhantomData<fn() -> (Proto, R, Tokens, MsgRest)>,
}
pub struct AsyncReceiving<Proto, R, Tokens, MsgRest, CP, Io>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
{
inner: HandshakeInner<Proto::Curve, Proto::Cipher, Proto::Hash, CP>,
stream: Io,
_marker: PhantomData<fn() -> (Proto, R, Tokens, MsgRest)>,
}
pub struct AsyncTransport<Proto: Protocol, Io> {
transport: Transport<Proto>,
stream: Io,
}
impl<Proto: Protocol, Io> AsyncTransport<Proto, Io> {
pub fn transport(&mut self) -> &mut Transport<Proto> {
&mut self.transport
}
pub fn stream(&mut self) -> &mut Io {
&mut self.stream
}
pub fn into_parts(self) -> (Transport<Proto>, Io) {
(self.transport, self.stream)
}
}
impl<Proto, CP, Io>
AsyncHandshake<
Proto,
Initiator,
<Proto::Pattern as Pattern>::PreMessages,
<Proto::Pattern as Pattern>::Messages,
CP,
Io,
>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
{
pub fn initiate(provider: CP, prologue: &[u8], stream: Io) -> Self {
AsyncHandshake {
inner: HandshakeInner::new::<Proto>(provider, prologue),
stream,
_marker: PhantomData,
}
}
}
impl<Proto, CP, Io>
AsyncHandshake<
Proto,
Responder,
<Proto::Pattern as Pattern>::PreMessages,
<Proto::Pattern as Pattern>::Messages,
CP,
Io,
>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
{
pub fn respond(provider: CP, prologue: &[u8], stream: Io) -> Self {
AsyncHandshake {
inner: HandshakeInner::new::<Proto>(provider, prologue),
stream,
_marker: PhantomData,
}
}
}
impl<P: WellFormed, Cu: DhCurve, Ci: Cipher, H: Hash> Noise<P, Cu, Ci, H> {
pub fn async_initiator<CP, Io>(
provider: CP,
prologue: &[u8],
stream: Io,
) -> AsyncHandshake<Self, Initiator, P::PreMessages, P::Messages, CP, Io>
where
CP: DhProviderAsync<Cu>,
{
AsyncHandshake::initiate(provider, prologue, stream)
}
pub fn async_responder<CP, Io>(
provider: CP,
prologue: &[u8],
stream: Io,
) -> AsyncHandshake<Self, Responder, P::PreMessages, P::Messages, CP, Io>
where
CP: DhProviderAsync<Cu>,
{
AsyncHandshake::respond(provider, prologue, stream)
}
}
impl<Proto, Tokens, Rest, Msgs, CP, Io>
AsyncHandshake<Proto, Initiator, Cons<Message<ToInitiator, Tokens>, Rest>, Msgs, CP, Io>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
{
pub fn set_rs(
mut self,
remote_static: <Proto::Curve as Curve>::PublicKey,
) -> AsyncHandshake<Proto, Initiator, Rest, Msgs, CP, Io> {
self.inner.symmetric.mix_hash(remote_static.as_ref());
self.inner.rs = Some(remote_static);
AsyncHandshake {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
}
}
}
impl<Proto, Tokens, Rest, Msgs, CP, Io>
AsyncHandshake<Proto, Responder, Cons<Message<ToInitiator, Tokens>, Rest>, Msgs, CP, Io>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
{
pub fn set_s(
mut self,
static_key: CP::PrivateKey,
) -> Result<AsyncHandshake<Proto, Responder, Rest, Msgs, CP, Io>, HandshakeError> {
let s_pub = self
.inner
.provider
.public_key(&static_key)
.map_err(|e| HandshakeError::Crypto(Box::new(e)))?;
self.inner.symmetric.mix_hash(s_pub.as_ref());
self.inner.s_pub = Some(s_pub);
self.inner.s = Some(static_key);
Ok(AsyncHandshake {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
})
}
}
impl<Proto, Tokens, Rest, Msgs, CP, Io>
AsyncHandshake<Proto, Initiator, Cons<Message<ToResponder, Tokens>, Rest>, Msgs, CP, Io>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
{
pub fn set_s(
mut self,
static_key: CP::PrivateKey,
) -> Result<AsyncHandshake<Proto, Initiator, Rest, Msgs, CP, Io>, HandshakeError> {
let s_pub = self
.inner
.provider
.public_key(&static_key)
.map_err(|e| HandshakeError::Crypto(Box::new(e)))?;
self.inner.symmetric.mix_hash(s_pub.as_ref());
self.inner.s_pub = Some(s_pub);
self.inner.s = Some(static_key);
Ok(AsyncHandshake {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
})
}
}
impl<Proto, Tokens, Rest, Msgs, CP, Io>
AsyncHandshake<Proto, Responder, Cons<Message<ToResponder, Tokens>, Rest>, Msgs, CP, Io>
where
Proto: Protocol,
CP: DhProviderAsync<Proto::Curve>,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
{
pub fn set_rs(
mut self,
remote_static: <Proto::Curve as Curve>::PublicKey,
) -> AsyncHandshake<Proto, Responder, Rest, Msgs, CP, Io> {
self.inner.symmetric.mix_hash(remote_static.as_ref());
self.inner.rs = Some(remote_static);
AsyncHandshake {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
}
}
}
impl<Proto, R, Next, More, MsgRest, Dir, CP, Io>
AsyncHandshake<Proto, R, Nil, Cons<Message<Dir, Cons<E, Cons<Next, More>>>, MsgRest>, CP, Io>
where
Proto: Protocol,
R: Role<SendDir = Dir>,
CP: DhProviderAsync<Proto::Curve>,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
Io: AsyncWrite + Unpin,
{
pub async fn e(
mut self,
) -> Result<AsyncSending<Proto, R, Cons<Next, More>, MsgRest, CP, Io>, HandshakeError> {
async_stream_e(&mut self.inner, &mut self.stream).await?;
Ok(AsyncSending {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
})
}
}
impl<Proto, R, NextMsg, MoreMsgs, Dir, CP, Io>
AsyncHandshake<Proto, R, Nil, Cons<Message<Dir, Cons<E, Nil>>, Cons<NextMsg, MoreMsgs>>, CP, Io>
where
Proto: Protocol,
R: Role<SendDir = Dir>,
CP: DhProviderAsync<Proto::Curve>,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
Io: AsyncWrite + Unpin,
{
pub async fn e(
mut self,
) -> Result<AsyncHandshake<Proto, R, Nil, Cons<NextMsg, MoreMsgs>, CP, Io>, HandshakeError>
{
async_stream_e(&mut self.inner, &mut self.stream).await?;
send_message_tail_async(&mut self.inner, &mut self.stream).await?;
Ok(AsyncHandshake {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
})
}
}
impl<Proto, R, Tokens, MsgRest, Dir, CP, Io>
AsyncHandshake<Proto, R, Nil, Cons<Message<Dir, Cons<Psk, Tokens>>, MsgRest>, CP, Io>
where
Proto: Protocol,
R: Role<SendDir = Dir>,
CP: DhProviderAsync<Proto::Curve>,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
{
pub async fn psk(
mut self,
psk_key: &crate::psk::Psk,
) -> Result<AsyncSending<Proto, R, Tokens, MsgRest, CP, Io>, HandshakeError> {
do_psk(&mut self.inner, psk_key)?;
Ok(AsyncSending {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
})
}
}
impl<Proto, R, Tokens, MsgRest, Dir, CP, Io>
AsyncHandshake<Proto, R, Nil, Cons<Message<Dir, Cons<S, Tokens>>, MsgRest>, CP, Io>
where
Proto: Protocol,
R: Role<SendDir = Dir>,
CP: DhProviderAsync<Proto::Curve>,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
Io: AsyncWrite + Unpin,
{
pub async fn s(
mut self,
static_key: CP::PrivateKey,
) -> Result<AsyncSending<Proto, R, Tokens, MsgRest, CP, Io>, HandshakeError> {
async_stream_s(&mut self.inner, &mut self.stream, static_key).await?;
Ok(AsyncSending {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
})
}
}
impl<Proto, R, Tokens, Rest, Dir, CP, Io>
AsyncHandshake<Proto, R, Nil, Cons<Message<Dir, Tokens>, Rest>, CP, Io>
where
Proto: Protocol,
R: Role<RecvDir = Dir>,
CP: DhProviderAsync<Proto::Curve>,
{
pub fn recv(self) -> AsyncReceiving<Proto, R, Tokens, Rest, CP, Io> {
AsyncReceiving {
inner: self.inner,
stream: self.stream,
_marker: PhantomData,
}
}
}
macro_rules! async_send_token {
(
role: $R:ty,
token: $Token:ty,
method: $method:ident ($($arg:ident : $arg_ty:ty),*),
bounds: [$($extra:tt)*],
doc: $doc:expr,
body: |$inner:ident, $stream:ident| { $($logic:tt)* }
) => {
impl<Proto, Next, More, MsgRest, CP, Io>
AsyncSending<Proto, $R, Cons<$Token, Cons<Next, More>>, MsgRest, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncWrite + Unpin,
$($extra)*
{
#[doc = $doc]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<AsyncSending<Proto, $R, Cons<Next, More>, MsgRest, CP, Io>, HandshakeError> {
{
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
}
Ok(AsyncSending { inner: self.inner, stream: self.stream, _marker: PhantomData })
}
}
impl<Proto, NextMsg, MoreMsgs, CP, Io>
AsyncSending<Proto, $R, Cons<$Token, Nil>, Cons<NextMsg, MoreMsgs>, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncWrite + Unpin,
$($extra)*
{
#[doc = $doc]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<AsyncHandshake<Proto, $R, Nil, Cons<NextMsg, MoreMsgs>, CP, Io>, HandshakeError> {
{
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
}
send_message_tail_async(&mut self.inner, &mut self.stream).await?;
Ok(AsyncHandshake { inner: self.inner, stream: self.stream, _marker: PhantomData })
}
}
impl<Proto, CP, Io>
AsyncSending<Proto, $R, Cons<$Token, Nil>, Nil, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncWrite + Unpin,
$($extra)*
{
#[doc = $doc]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<AsyncTransport<Proto, Io>, HandshakeError> {
{
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
}
send_message_tail_async(&mut self.inner, &mut self.stream).await?;
let transport = recv_to_transport::<Proto, $R, CP>(self.inner);
Ok(AsyncTransport { transport, stream: self.stream })
}
}
};
}
macro_rules! async_recv_token {
(
role: $R:ty,
token: $Token:ty,
method: $method:ident ($($arg:ident : $arg_ty:ty),*),
bounds: [$($extra:tt)*],
doc: $doc:expr,
body: |$inner:ident, $stream:ident| { $($logic:tt)* }
) => {
impl<Proto, Next, More, MsgRest, CP, Io>
AsyncReceiving<Proto, $R, Cons<$Token, Cons<Next, More>>, MsgRest, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncRead + Unpin,
$($extra)*
{
#[doc = $doc]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<AsyncReceiving<Proto, $R, Cons<Next, More>, MsgRest, CP, Io>, HandshakeError> {
{
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
}
Ok(AsyncReceiving { inner: self.inner, stream: self.stream, _marker: PhantomData })
}
}
impl<Proto, NextMsg, MoreMsgs, CP, Io>
AsyncReceiving<Proto, $R, Cons<$Token, Nil>, Cons<NextMsg, MoreMsgs>, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncRead + Unpin,
$($extra)*
{
#[doc = $doc]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<AsyncHandshake<Proto, $R, Nil, Cons<NextMsg, MoreMsgs>, CP, Io>, HandshakeError> {
{
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
}
recv_message_tail_async(&mut self.inner, &mut self.stream).await?;
Ok(AsyncHandshake { inner: self.inner, stream: self.stream, _marker: PhantomData })
}
}
impl<Proto, CP, Io>
AsyncReceiving<Proto, $R, Cons<$Token, Nil>, Nil, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncRead + Unpin,
$($extra)*
{
#[doc = $doc]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<AsyncTransport<Proto, Io>, HandshakeError> {
{
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
}
recv_message_tail_async(&mut self.inner, &mut self.stream).await?;
let transport = recv_to_transport::<Proto, $R, CP>(self.inner);
Ok(AsyncTransport { transport, stream: self.stream })
}
}
};
}
macro_rules! async_recv_reveal_token {
(
role: $R:ty,
token: $Token:ty,
method: $method:ident ($($arg:ident : $arg_ty:ty),*),
bounds: [$($extra:tt)*],
doc: $doc:expr,
body: |$inner:ident, $stream:ident| { $($logic:tt)* }
) => {
impl<Proto, Next, More, MsgRest, CP, Io>
AsyncReceiving<Proto, $R, Cons<$Token, Cons<Next, More>>, MsgRest, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncRead + Unpin,
$($extra)*
{
#[doc = $doc]
#[allow(clippy::type_complexity)]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<(<Proto::Curve as Curve>::PublicKey, AsyncReceiving<Proto, $R, Cons<Next, More>, MsgRest, CP, Io>), HandshakeError> {
let revealed = {
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
};
Ok((revealed, AsyncReceiving { inner: self.inner, stream: self.stream, _marker: PhantomData }))
}
}
impl<Proto, NextMsg, MoreMsgs, CP, Io>
AsyncReceiving<Proto, $R, Cons<$Token, Nil>, Cons<NextMsg, MoreMsgs>, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncRead + Unpin,
$($extra)*
{
#[doc = $doc]
#[allow(clippy::type_complexity)]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<(<Proto::Curve as Curve>::PublicKey, AsyncHandshake<Proto, $R, Nil, Cons<NextMsg, MoreMsgs>, CP, Io>), HandshakeError> {
let revealed = {
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
};
recv_message_tail_async(&mut self.inner, &mut self.stream).await?;
Ok((revealed, AsyncHandshake { inner: self.inner, stream: self.stream, _marker: PhantomData }))
}
}
impl<Proto, CP, Io>
AsyncReceiving<Proto, $R, Cons<$Token, Nil>, Nil, CP, Io>
where
Proto: Protocol,
<Proto::Curve as Curve>::PublicKey: AsRef<[u8]>,
CP: DhProviderAsync<Proto::Curve>,
Io: AsyncRead + Unpin,
$($extra)*
{
#[doc = $doc]
#[allow(clippy::type_complexity)]
pub async fn $method(
mut self, $($arg: $arg_ty,)*
) -> Result<(<Proto::Curve as Curve>::PublicKey, AsyncTransport<Proto, Io>), HandshakeError> {
let revealed = {
let $inner = &mut self.inner;
let $stream = &mut self.stream;
$($logic)*
};
recv_message_tail_async(&mut self.inner, &mut self.stream).await?;
let transport = recv_to_transport::<Proto, $R, CP>(self.inner);
Ok((revealed, AsyncTransport { transport, stream: self.stream }))
}
}
};
}
async_send_token! {
role: Initiator, token: E, method: e(), bounds: [],
doc: "Process the `e` token: generate a fresh ephemeral key, write its \
public key to the wire, and mix it into the handshake hash (also \
mixing it into the chaining key in a PSK pattern).",
body: |inner, stream| { async_stream_e(inner, stream).await?; }
}
async_send_token! {
role: Responder, token: E, method: e(), bounds: [],
doc: "Process the `e` token: generate a fresh ephemeral key, write its \
public key to the wire, and mix it into the handshake hash (also \
mixing it into the chaining key in a PSK pattern).",
body: |inner, stream| { async_stream_e(inner, stream).await?; }
}
async_recv_reveal_token! {
role: Initiator, token: E, method: e(), bounds: [],
doc: "Process the `e` token: read the peer's ephemeral public key from \
the wire and mix it into the handshake hash (also mixing it into \
the chaining key in a PSK pattern). Returns the revealed key.",
body: |inner, stream| { async_read_e(inner, stream).await? }
}
async_recv_reveal_token! {
role: Responder, token: E, method: e(), bounds: [],
doc: "Process the `e` token: read the peer's ephemeral public key from \
the wire and mix it into the handshake hash (also mixing it into \
the chaining key in a PSK pattern). Returns the revealed key.",
body: |inner, stream| { async_read_e(inner, stream).await? }
}
async_send_token! {
role: Initiator, token: S, method: s(static_key: CP::PrivateKey), bounds: [],
doc: "Process the `s` token: write our local static public key to the \
wire — encrypted once a key has been established by a prior DH \
token — and mix it into the handshake hash.",
body: |inner, stream| { async_stream_s(inner, stream, static_key).await?; }
}
async_send_token! {
role: Responder, token: S, method: s(static_key: CP::PrivateKey), bounds: [],
doc: "Process the `s` token: write our local static public key to the \
wire — encrypted once a key has been established by a prior DH \
token — and mix it into the handshake hash.",
body: |inner, stream| { async_stream_s(inner, stream, static_key).await?; }
}
async_recv_reveal_token! {
role: Initiator, token: S, method: s(), bounds: [],
doc: "Process the `s` token: read the peer's static public key from the \
wire — decrypting it once a key has been established by a prior DH \
token — and mix it into the handshake hash. Returns the revealed key.",
body: |inner, stream| { async_read_s(inner, stream).await? }
}
async_recv_reveal_token! {
role: Responder, token: S, method: s(), bounds: [],
doc: "Process the `s` token: read the peer's static public key from the \
wire — decrypting it once a key has been established by a prior DH \
token — and mix it into the handshake hash. Returns the revealed key.",
body: |inner, stream| { async_read_s(inner, stream).await? }
}
async_send_token! {
role: Initiator, token: Ee, method: ee(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `ee` token: perform the Diffie–Hellman between our \
local ephemeral and the remote ephemeral, and mix the shared \
secret into the chaining key. Writes nothing to the wire; \
subsequent payloads are encrypted under the advanced key.",
body: |inner, _stream| { do_ee(inner).await?; }
}
async_send_token! {
role: Responder, token: Ee, method: ee(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `ee` token: perform the Diffie–Hellman between our \
local ephemeral and the remote ephemeral, and mix the shared \
secret into the chaining key. Writes nothing to the wire; \
subsequent payloads are encrypted under the advanced key.",
body: |inner, _stream| { do_ee(inner).await?; }
}
async_recv_token! {
role: Initiator, token: Ee, method: ee(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `ee` token: perform the Diffie–Hellman between our \
local ephemeral and the remote ephemeral, and mix the shared \
secret into the chaining key. Reads nothing from the wire; \
subsequent payloads are decrypted under the advanced key.",
body: |inner, _stream| { do_ee(inner).await?; }
}
async_recv_token! {
role: Responder, token: Ee, method: ee(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `ee` token: perform the Diffie–Hellman between our \
local ephemeral and the remote ephemeral, and mix the shared \
secret into the chaining key. Reads nothing from the wire; \
subsequent payloads are decrypted under the advanced key.",
body: |inner, _stream| { do_ee(inner).await?; }
}
async_send_token! {
role: Initiator, token: Es, method: es(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `es` token (initiator): perform the Diffie–Hellman \
between our local ephemeral and the remote static, and mix the \
shared secret into the chaining key. Writes nothing to the wire; \
subsequent payloads are encrypted under the advanced key.",
body: |inner, _stream| { do_es_initiator(inner).await?; }
}
async_recv_token! {
role: Initiator, token: Es, method: es(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `es` token (initiator): perform the Diffie–Hellman \
between our local ephemeral and the remote static, and mix the \
shared secret into the chaining key. Reads nothing from the wire; \
subsequent payloads are decrypted under the advanced key.",
body: |inner, _stream| { do_es_initiator(inner).await?; }
}
async_send_token! {
role: Responder, token: Es, method: es(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `es` token (responder): perform the Diffie–Hellman \
between our local static and the remote ephemeral, and mix the \
shared secret into the chaining key. Writes nothing to the wire; \
subsequent payloads are encrypted under the advanced key.",
body: |inner, _stream| { do_es_responder(inner).await?; }
}
async_recv_token! {
role: Responder, token: Es, method: es(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `es` token (responder): perform the Diffie–Hellman \
between our local static and the remote ephemeral, and mix the \
shared secret into the chaining key. Reads nothing from the wire; \
subsequent payloads are decrypted under the advanced key.",
body: |inner, _stream| { do_es_responder(inner).await?; }
}
async_send_token! {
role: Initiator, token: Se, method: se(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `se` token (initiator): perform the Diffie–Hellman \
between our local static and the remote ephemeral, and mix the \
shared secret into the chaining key. Writes nothing to the wire; \
subsequent payloads are encrypted under the advanced key.",
body: |inner, _stream| { do_se_initiator(inner).await?; }
}
async_recv_token! {
role: Initiator, token: Se, method: se(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `se` token (initiator): perform the Diffie–Hellman \
between our local static and the remote ephemeral, and mix the \
shared secret into the chaining key. Reads nothing from the wire; \
subsequent payloads are decrypted under the advanced key.",
body: |inner, _stream| { do_se_initiator(inner).await?; }
}
async_send_token! {
role: Responder, token: Se, method: se(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `se` token (responder): perform the Diffie–Hellman \
between our local ephemeral and the remote static, and mix the \
shared secret into the chaining key. Writes nothing to the wire; \
subsequent payloads are encrypted under the advanced key.",
body: |inner, _stream| { do_se_responder(inner).await?; }
}
async_recv_token! {
role: Responder, token: Se, method: se(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `se` token (responder): perform the Diffie–Hellman \
between our local ephemeral and the remote static, and mix the \
shared secret into the chaining key. Reads nothing from the wire; \
subsequent payloads are decrypted under the advanced key.",
body: |inner, _stream| { do_se_responder(inner).await?; }
}
async_send_token! {
role: Initiator, token: Ss, method: ss(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `ss` token: perform the Diffie–Hellman between our \
local static and the remote static, and mix the shared secret \
into the chaining key. Writes nothing to the wire; subsequent \
payloads are encrypted under the advanced key.",
body: |inner, _stream| { do_ss(inner).await?; }
}
async_send_token! {
role: Responder, token: Ss, method: ss(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `ss` token: perform the Diffie–Hellman between our \
local static and the remote static, and mix the shared secret \
into the chaining key. Writes nothing to the wire; subsequent \
payloads are encrypted under the advanced key.",
body: |inner, _stream| { do_ss(inner).await?; }
}
async_recv_token! {
role: Initiator, token: Ss, method: ss(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `ss` token: perform the Diffie–Hellman between our \
local static and the remote static, and mix the shared secret \
into the chaining key. Reads nothing from the wire; subsequent \
payloads are decrypted under the advanced key.",
body: |inner, _stream| { do_ss(inner).await?; }
}
async_recv_token! {
role: Responder, token: Ss, method: ss(), bounds: [<Proto::Curve as DhCurve>::SharedSecret: AsRef<[u8]>,],
doc: "Process the `ss` token: perform the Diffie–Hellman between our \
local static and the remote static, and mix the shared secret \
into the chaining key. Reads nothing from the wire; subsequent \
payloads are decrypted under the advanced key.",
body: |inner, _stream| { do_ss(inner).await?; }
}
async_send_token! {
role: Initiator, token: Psk, method: psk(psk_key: &crate::psk::Psk), bounds: [],
doc: "Process the `psk` token: mix the 32-byte pre-shared key into both \
the chaining key and the handshake hash (Noise's `MixKeyAndHash`). \
Writes nothing to the wire; subsequent payloads are encrypted \
under the advanced key.",
body: |inner, _stream| { do_psk(inner, psk_key)?; }
}
async_send_token! {
role: Responder, token: Psk, method: psk(psk_key: &crate::psk::Psk), bounds: [],
doc: "Process the `psk` token: mix the 32-byte pre-shared key into both \
the chaining key and the handshake hash (Noise's `MixKeyAndHash`). \
Writes nothing to the wire; subsequent payloads are encrypted \
under the advanced key.",
body: |inner, _stream| { do_psk(inner, psk_key)?; }
}
async_recv_token! {
role: Initiator, token: Psk, method: psk(psk_key: &crate::psk::Psk), bounds: [],
doc: "Process the `psk` token: mix the 32-byte pre-shared key into both \
the chaining key and the handshake hash (Noise's `MixKeyAndHash`). \
Reads nothing from the wire; subsequent payloads are decrypted \
under the advanced key.",
body: |inner, _stream| { do_psk(inner, psk_key)?; }
}
async_recv_token! {
role: Responder, token: Psk, method: psk(psk_key: &crate::psk::Psk), bounds: [],
doc: "Process the `psk` token: mix the 32-byte pre-shared key into both \
the chaining key and the handshake hash (Noise's `MixKeyAndHash`). \
Reads nothing from the wire; subsequent payloads are decrypted \
under the advanced key.",
body: |inner, _stream| { do_psk(inner, psk_key)?; }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::noise::{Blake2b, ChaChaPoly, Initiator, Noise, P256, Responder, pattern};
use crate::provider::EphemeralOnly;
use crate::provider::ProviderExt;
use crate::psk::Psk;
use rand::{SeedableRng, rngs::StdRng};
use std::io::Cursor;
type Seal = Noise<pattern::N, P256, ChaChaPoly, Blake2b>;
type Channel = Noise<pattern::IKpsk1, P256, ChaChaPoly, Blake2b>;
type NoiseK = Noise<pattern::K, P256, ChaChaPoly, Blake2b>;
#[tokio::test]
async fn n_async_seal_open_roundtrip() {
let mut provider = EphemeralOnly::new(StdRng::from_os_rng());
let recipient_static = provider.generate::<P256>().unwrap();
let recipient_pub = provider.public(&recipient_static).unwrap();
let sealer = AsyncHandshake::<Seal, Initiator, _, _, _, _>::initiate(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
Vec::<u8>::new(),
)
.set_rs(recipient_pub);
let sealer_done = sealer.e().await.unwrap().es().await.unwrap();
let (mut send_transport, wire) = sealer_done.into_parts();
assert_eq!(wire.len(), 81);
let payload = [0x42u8; 32];
let mut sealed = [0u8; 48];
let sealed_len = send_transport.send(&payload, &mut sealed).unwrap();
let opener = AsyncHandshake::<Seal, Responder, _, _, _, _>::respond(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
Cursor::new(wire),
)
.set_s(recipient_static)
.unwrap();
let (_revealed_e, recv) = opener.recv().e().await.unwrap();
let mut recv_transport = recv.es().await.unwrap();
assert_eq!(
send_transport.session_id(),
recv_transport.transport().session_id()
);
let mut opened = [0u8; 32];
let opened_len = recv_transport
.transport()
.receive(&sealed[..sealed_len], &mut opened)
.unwrap();
assert_eq!(opened_len, 32);
assert_eq!(opened, payload);
}
#[tokio::test]
async fn n_async_constructors_roundtrip() {
type N = Seal;
let mut provider = EphemeralOnly::new(StdRng::from_os_rng());
let recipient_static = provider.generate::<P256>().unwrap();
let recipient_pub = provider.public(&recipient_static).unwrap();
let sealer = N::async_initiator(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
Vec::<u8>::new(),
)
.set_rs(recipient_pub);
let (mut send_transport, wire) = sealer.e().await.unwrap().es().await.unwrap().into_parts();
assert_eq!(wire.len(), 81);
let payload = [0x42u8; 32];
let mut sealed = [0u8; 48];
let sealed_len = send_transport.send(&payload, &mut sealed).unwrap();
let opener = N::async_responder(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
Cursor::new(wire),
)
.set_s(recipient_static)
.unwrap();
let (_e, recv) = opener.recv().e().await.unwrap();
let mut recv_transport = recv.es().await.unwrap();
let mut opened = [0u8; 32];
let opened_len = recv_transport
.transport()
.receive(&sealed[..sealed_len], &mut opened)
.unwrap();
assert_eq!(opened_len, 32);
assert_eq!(opened, payload);
}
#[tokio::test]
async fn n_async_tampered_ephemeral_rejected() {
let mut provider = EphemeralOnly::new(StdRng::from_os_rng());
let recipient_static = provider.generate::<P256>().unwrap();
let recipient_pub = provider.public(&recipient_static).unwrap();
let sealer = AsyncHandshake::<Seal, Initiator, _, _, _, _>::initiate(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
Vec::<u8>::new(),
)
.set_rs(recipient_pub);
let (_t, mut wire) = sealer.e().await.unwrap().es().await.unwrap().into_parts();
wire[1] ^= 0xFF;
let opener = AsyncHandshake::<Seal, Responder, _, _, _, _>::respond(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
Cursor::new(wire),
)
.set_s(recipient_static)
.unwrap();
match opener.recv().e().await {
Err(_) => {}
Ok((_e, recv)) => assert!(recv.es().await.is_err()),
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[tokio::test]
async fn n_async_seal_open_with_secure_enclave_provider() {
use crate::provider::CryptoKeyProviderAsync;
use crate::provider::apple::AppleSecureEnclave;
let mut provider = AppleSecureEnclave::new("uk.co.example.hiss-test");
let recipient_static =
CryptoKeyProviderAsync::<P256>::generate_ephemeral_key_async(&mut provider)
.await
.unwrap();
let recipient_pub = provider.public(&recipient_static).unwrap();
let sealer = AsyncHandshake::<Seal, Initiator, _, _, _, _>::initiate(
provider.clone(),
&[],
Vec::<u8>::new(),
)
.set_rs(recipient_pub);
let (mut send_transport, wire) = sealer.e().await.unwrap().es().await.unwrap().into_parts();
assert_eq!(wire.len(), 81);
let payload = [0x42u8; 32];
let mut sealed = [0u8; 48];
let sealed_len = send_transport.send(&payload, &mut sealed).unwrap();
let opener = AsyncHandshake::<Seal, Responder, _, _, _, _>::respond(
provider.clone(),
&[],
Cursor::new(wire),
)
.set_s(recipient_static)
.unwrap();
let (_revealed_e, recv) = opener.recv().e().await.unwrap();
let mut recv_transport = recv.es().await.unwrap();
assert_eq!(
send_transport.session_id(),
recv_transport.transport().session_id()
);
let mut opened = [0u8; 32];
let opened_len = recv_transport
.transport()
.receive(&sealed[..sealed_len], &mut opened)
.unwrap();
assert_eq!(&opened[..opened_len], &payload);
}
#[tokio::test]
async fn ikpsk1_async_round_trip() {
let mut provider = EphemeralOnly::new(StdRng::from_os_rng());
let initiator_static = provider.generate::<P256>().unwrap();
let initiator_pub = provider.public(&initiator_static).unwrap();
let responder_static = provider.generate::<P256>().unwrap();
let responder_pub = provider.public(&responder_static).unwrap();
let psk = Psk::from_bytes([0xAA; 32]);
let (init_stream, resp_stream) = tokio::io::duplex(4096);
let i_hs = AsyncHandshake::<Channel, Initiator, _, _, _, _>::initiate(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
init_stream,
)
.set_rs(responder_pub);
let i_hs = i_hs
.e()
.await
.unwrap()
.es()
.await
.unwrap()
.s(initiator_static)
.await
.unwrap()
.ss()
.await
.unwrap()
.psk(&psk)
.await
.unwrap();
let r_hs = AsyncHandshake::<Channel, Responder, _, _, _, _>::respond(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
resp_stream,
)
.set_s(responder_static)
.unwrap();
let (_e, recv) = r_hs.recv().e().await.unwrap();
let recv = recv.es().await.unwrap();
let (revealed_i_pub, recv) = recv.s().await.unwrap();
assert_eq!(revealed_i_pub, initiator_pub);
let recv = recv.ss().await.unwrap();
let r_hs = recv.psk(&psk).await.unwrap();
let r_transport = r_hs
.e()
.await
.unwrap()
.ee()
.await
.unwrap()
.se()
.await
.unwrap();
let (_revealed_r_e, recv) = i_hs.recv().e().await.unwrap();
let i_transport = recv.ee().await.unwrap().se().await.unwrap();
assert_eq!(
i_transport.transport.session_id(),
r_transport.transport.session_id()
);
}
#[tokio::test]
async fn k_async_round_trip() {
let mut provider = EphemeralOnly::new(StdRng::from_os_rng());
let alice_static = provider.generate::<P256>().unwrap();
let alice_pub = provider.public(&alice_static).unwrap();
let bob_static = provider.generate::<P256>().unwrap();
let bob_pub = provider.public(&bob_static).unwrap();
let payload = [0x42u8; 32];
let sealer = AsyncHandshake::<NoiseK, Initiator, _, _, _, _>::initiate(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
Vec::<u8>::new(),
)
.set_s(alice_static)
.unwrap()
.set_rs(bob_pub);
let (mut send_transport, wire) = sealer
.e()
.await
.unwrap()
.es()
.await
.unwrap()
.ss()
.await
.unwrap()
.into_parts();
assert_eq!(wire.len(), 81);
let mut sealed = [0u8; 48];
let n = send_transport.send(&payload, &mut sealed).unwrap();
let opener = AsyncHandshake::<NoiseK, Responder, _, _, _, _>::respond(
EphemeralOnly::new(StdRng::from_os_rng()),
&[],
Cursor::new(wire),
)
.set_rs(alice_pub)
.set_s(bob_static)
.unwrap();
let (_e, recv) = opener.recv().e().await.unwrap();
let mut recv_transport = recv.es().await.unwrap().ss().await.unwrap();
assert_eq!(
send_transport.session_id(),
recv_transport.transport().session_id()
);
let mut opened = [0u8; 32];
let on = recv_transport
.transport()
.receive(&sealed[..n], &mut opened)
.unwrap();
assert_eq!(&opened[..on], &payload);
}
}