#![expect(
clippy::type_complexity,
reason = "Using the type definitions would obscure the very types I'm trying to show"
)]
use crypto_secretstream::{Header, Key, PullStream, PushStream, Tag};
use rand::rngs::OsRng;
use snow::{HandshakeState, Keypair};
use std::{fmt::Debug, marker::PhantomData};
use tracing::error;
use crate::{Error, crypto::write_stream_id};
const STREAM_ID_LENGTH: usize = 32;
const RAW_HEADER_MSG_LEN: usize = STREAM_ID_LENGTH + Header::BYTES;
const SNOW_CIPHERKEYLEN: usize = 32;
pub const PUBLIC_KEYLEN: usize = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HandshakePattern {
#[default]
IK,
XX,
}
#[derive(Debug)]
pub struct IK;
#[derive(Debug)]
pub struct XX;
pub struct SecStream<Step> {
is_initiator: bool,
pattern: HandshakePattern, state: HandshakeState,
local_public_key: [u8; PUBLIC_KEYLEN],
msg_buf: [u8; 1024],
step: Step,
}
impl<Step: Debug> std::fmt::Debug for SecStream<Step> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SecStream")
.field("is_initiator", &self.is_initiator)
.field("step", &self.step)
.finish()
}
}
impl<Step> SecStream<Step> {
pub fn split_handshake(&mut self) -> ([u8; SNOW_CIPHERKEYLEN], [u8; SNOW_CIPHERKEYLEN]) {
let (a, b) = self.state.dangerously_get_raw_split();
if self.is_initiator { (a, b) } else { (b, a) }
}
pub fn get_local_public_key(&self) -> [u8; PUBLIC_KEYLEN] {
self.local_public_key
}
pub fn pattern(&self) -> HandshakePattern {
self.pattern
}
pub fn get_remote_static(&self) -> Option<[u8; PUBLIC_KEYLEN]> {
self.state.get_remote_static().map(|bytes| {
bytes
.try_into().inspect_err(|error| error!(?error, "snow gave us a key with the wrong size? Expected length = [{PUBLIC_KEYLEN}] but got length = [{}]", bytes.len()))
.expect("snow gave us a key with the wrong size?")
})
}
pub fn is_initiator(&self) -> bool {
self.is_initiator
}
}
pub struct Initiator<Pattern, Step> {
_pattern: PhantomData<Pattern>,
_step: PhantomData<Step>,
}
impl<Pattern: 'static, Step: 'static> Debug for Initiator<Pattern, Step> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let pattern = std::any::type_name::<Pattern>()
.rsplit("::")
.next()
.unwrap_or("?");
let step = std::any::type_name::<Step>()
.rsplit("::")
.next()
.unwrap_or("?");
write!(f, "Initiator[{pattern}]({})", step)
}
}
pub struct Responder<Pattern, Step> {
_pattern: PhantomData<Pattern>,
_step: PhantomData<Step>,
}
impl<Pattern: 'static, Step: 'static> Debug for Responder<Pattern, Step> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let pattern = std::any::type_name::<Pattern>()
.rsplit("::")
.next()
.unwrap_or("?");
let step = std::any::type_name::<Step>()
.rsplit("::")
.next()
.unwrap_or("?");
write!(f, "Responder[{pattern}]({})", step)
}
}
#[derive(Debug)]
pub struct Start;
#[derive(Debug)]
pub struct HsMsgSent;
#[derive(Debug)]
pub struct InitiatorXxFinalMsg;
#[derive(Debug)]
pub struct ResponderXxReceivedFirst;
#[derive(Debug)]
pub struct ResponderXxAwaitingFinal;
#[derive(Debug)]
pub struct HsDone;
pub struct EncryptorReady {
rx: Key,
pusher: PushStream,
handshake_hash: Vec<u8>,
}
pub struct Ready {
puller: PullStream,
pusher: PushStream,
handshake_hash: Vec<u8>,
}
impl Debug for EncryptorReady {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EncryptorReady").finish()
}
}
impl Debug for Ready {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ready").finish()
}
}
pub mod hc_specific {
use crate::Error;
use std::sync::LazyLock;
pub use snow::Keypair;
use snow::{
Builder,
params::{BaseChoice, HandshakeChoice, NoiseParams},
resolvers::{DefaultResolver, FallbackResolver},
};
const IK_PARAM_STR: &str = "Noise_IK_Ed25519_ChaChaPoly_BLAKE2b";
const XX_PARAM_STR: &str = "Noise_XX_Ed25519_ChaChaPoly_BLAKE2b";
static IK_NOISE_PARAMS: LazyLock<NoiseParams> = LazyLock::new(|| {
NoiseParams::new(
IK_PARAM_STR.to_string(),
BaseChoice::Noise,
HandshakeChoice {
pattern: snow::params::HandshakePattern::IK,
modifiers: snow::params::HandshakeModifierList { list: vec![] },
},
snow::params::DHChoice::Curve25519,
snow::params::CipherChoice::ChaChaPoly,
snow::params::HashChoice::Blake2b,
)
});
static XX_NOISE_PARAMS: LazyLock<NoiseParams> = LazyLock::new(|| {
NoiseParams::new(
XX_PARAM_STR.to_string(),
BaseChoice::Noise,
HandshakeChoice {
pattern: snow::params::HandshakePattern::XX,
modifiers: snow::params::HandshakeModifierList { list: vec![] },
},
snow::params::DHChoice::Curve25519,
snow::params::CipherChoice::ChaChaPoly,
snow::params::HashChoice::Blake2b,
)
});
fn noise_params(pattern: crate::HandshakePattern) -> &'static NoiseParams {
match pattern {
crate::HandshakePattern::IK => &IK_NOISE_PARAMS,
crate::HandshakePattern::XX => &XX_NOISE_PARAMS,
}
}
pub(super) fn builder(pattern: crate::HandshakePattern) -> Builder<'static> {
let params = noise_params(pattern);
Builder::with_resolver(
params.clone(),
Box::new(FallbackResolver::new(
Box::<crate::crypto::CurveResolver>::default(),
Box::<DefaultResolver>::default(),
)),
)
}
pub fn generate_keypair() -> Result<Keypair, Error> {
Ok(builder(crate::HandshakePattern::default()).generate_keypair()?)
}
}
impl SecStream<Initiator<IK, Start>> {
pub fn new_initiator_ik(
remote_public_key: &[u8; PUBLIC_KEYLEN],
prologue: &[u8],
) -> Result<Self, Error> {
let key_pair = hc_specific::generate_keypair()?;
let state = hc_specific::builder(HandshakePattern::IK)
.prologue(prologue)?
.local_private_key(&key_pair.private)?
.remote_public_key(remote_public_key.as_slice())?
.build_initiator()?;
Ok(Self {
is_initiator: true,
pattern: HandshakePattern::IK,
state,
local_public_key: key_pair
.public
.try_into()
.expect("Wrong sized key from snow?"),
msg_buf: [0; 1024],
step: Initiator {
_pattern: PhantomData,
_step: PhantomData,
},
})
}
pub fn write_msg(
mut self,
payload: Option<&[u8]>,
) -> Result<(SecStream<Initiator<IK, HsMsgSent>>, Vec<u8>), Error> {
let payload = payload.unwrap_or_default();
let len = self.state.write_message(payload, &mut self.msg_buf)?;
let msg = self.msg_buf[..len].to_vec();
let Self {
is_initiator,
pattern,
state,
msg_buf,
local_public_key,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Initiator {
_pattern: PhantomData,
_step: PhantomData,
},
},
msg,
))
}
}
impl SecStream<Initiator<XX, Start>> {
pub fn new_initiator_xx(prologue: &[u8]) -> Result<Self, Error> {
let key_pair = hc_specific::generate_keypair()?;
let state = hc_specific::builder(HandshakePattern::XX)
.prologue(prologue)?
.local_private_key(&key_pair.private)?
.build_initiator()?;
Ok(Self {
is_initiator: true,
pattern: HandshakePattern::XX,
state,
local_public_key: key_pair
.public
.try_into()
.expect("Wrong sized key from snow?"),
msg_buf: [0; 1024],
step: Initiator {
_pattern: PhantomData,
_step: PhantomData,
},
})
}
pub fn write_msg(
mut self,
payload: Option<&[u8]>,
) -> Result<(SecStream<Initiator<XX, HsMsgSent>>, Vec<u8>), Error> {
let payload = payload.unwrap_or_default();
let len = self.state.write_message(payload, &mut self.msg_buf)?;
let msg = self.msg_buf[..len].to_vec();
let Self {
is_initiator,
pattern,
state,
msg_buf,
local_public_key,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Initiator {
_pattern: PhantomData,
_step: PhantomData,
},
},
msg,
))
}
}
impl SecStream<Responder<IK, Start>> {
pub fn new_responder_ik(keypair: &Keypair, prologue: &[u8]) -> Result<Self, Error> {
let state = hc_specific::builder(HandshakePattern::IK)
.prologue(prologue)?
.local_private_key(&keypair.private)?
.build_responder()?;
Ok(Self {
is_initiator: false,
pattern: HandshakePattern::IK,
state,
local_public_key: keypair
.public
.clone()
.try_into()
.expect("Wrong sized key from snow?"),
msg_buf: [0; 1024],
step: Responder {
_pattern: PhantomData,
_step: PhantomData,
},
})
}
pub fn read_msg(
mut self,
msg: &[u8],
) -> Result<(SecStream<Responder<IK, HsDone>>, Vec<u8>), Error> {
let len = self.state.read_message(msg, &mut self.msg_buf)?;
let payload = &self.msg_buf[..len];
let Self {
is_initiator,
pattern,
state,
msg_buf,
local_public_key,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Responder {
_pattern: PhantomData,
_step: PhantomData,
},
},
payload.to_vec(),
))
}
pub fn read_and_write_msg(
self,
msg: &[u8],
) -> Result<(SecStream<EncryptorReady>, [Vec<u8>; 2]), Error> {
let (self2, _rx_payload) = self.read_msg(msg)?;
self2.write_msg(Some(&[]))
}
}
impl SecStream<Responder<XX, Start>> {
pub fn new_responder_xx(keypair: &Keypair, prologue: &[u8]) -> Result<Self, Error> {
let state = hc_specific::builder(HandshakePattern::XX)
.prologue(prologue)?
.local_private_key(&keypair.private)?
.build_responder()?;
Ok(Self {
is_initiator: false,
pattern: HandshakePattern::XX,
state,
local_public_key: keypair
.public
.clone()
.try_into()
.expect("Wrong sized key from snow?"),
msg_buf: [0; 1024],
step: Responder {
_pattern: PhantomData,
_step: PhantomData,
},
})
}
pub fn read_msg(
mut self,
msg: &[u8],
) -> Result<(SecStream<Responder<XX, ResponderXxReceivedFirst>>, Vec<u8>), Error> {
let len = self.state.read_message(msg, &mut self.msg_buf)?;
let payload = &self.msg_buf[..len];
let Self {
is_initiator,
pattern,
state,
msg_buf,
local_public_key,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Responder {
_pattern: PhantomData,
_step: PhantomData,
},
},
payload.to_vec(),
))
}
}
impl SecStream<Responder<XX, ResponderXxReceivedFirst>> {
pub fn write_msg(
mut self,
payload: Option<&[u8]>,
) -> Result<
(
SecStream<Responder<XX, ResponderXxAwaitingFinal>>,
[Vec<u8>; 2],
),
Error,
> {
let payload = payload.unwrap_or_default();
let len = self.state.write_message(payload, &mut self.msg_buf)?;
let hs_msg = self.msg_buf[..len].to_vec();
assert!(
!self.state.is_handshake_finished(),
"XX handshake should not be finished yet"
);
let Self {
is_initiator,
pattern,
state,
msg_buf,
local_public_key,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Responder {
_pattern: PhantomData,
_step: PhantomData,
},
},
[hs_msg, Vec::new()], ))
}
}
impl SecStream<Responder<XX, ResponderXxAwaitingFinal>> {
pub fn read_msg(
mut self,
msg: &[u8],
) -> Result<(SecStream<Responder<XX, HsDone>>, Vec<u8>), Error> {
let len = self.state.read_message(msg, &mut self.msg_buf)?;
let payload = &self.msg_buf[..len];
assert!(
self.state.is_handshake_finished(),
"XX handshake should be finished after third message"
);
let Self {
is_initiator,
pattern,
state,
msg_buf,
local_public_key,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Responder {
_pattern: PhantomData,
_step: PhantomData,
},
},
payload.to_vec(),
))
}
}
impl SecStream<Responder<IK, HsDone>> {
pub fn write_msg(
mut self,
payload: Option<&[u8]>,
) -> Result<(SecStream<EncryptorReady>, [Vec<u8>; 2]), Error> {
let payload = payload.unwrap_or_default();
let len = self.state.write_message(payload, &mut self.msg_buf)?;
let hs_msg = self.msg_buf[..len].to_vec();
assert!(
self.state.is_handshake_finished(),
"IK handshake should be finished after responder's message"
);
let handshake_hash = self.state.get_handshake_hash().to_vec();
let mut msg: [u8; RAW_HEADER_MSG_LEN] = [0; RAW_HEADER_MSG_LEN];
write_stream_id(
&handshake_hash,
self.is_initiator,
&mut msg[..STREAM_ID_LENGTH],
);
let (tx, rx) = self.split_handshake();
let (header, pusher) = PushStream::init(OsRng, &Key::from(tx));
msg[STREAM_ID_LENGTH..].copy_from_slice(header.as_ref());
let Self {
is_initiator,
pattern,
state,
msg_buf,
local_public_key,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: EncryptorReady {
rx: Key::from(rx),
pusher,
handshake_hash,
},
},
[hs_msg, msg.to_vec()],
))
}
}
impl SecStream<Responder<XX, HsDone>> {
pub fn write_msg(mut self) -> Result<(SecStream<EncryptorReady>, Vec<u8>), Error> {
assert!(
self.state.is_handshake_finished(),
"XX handshake should be finished before sending setup"
);
let handshake_hash = self.state.get_handshake_hash().to_vec();
let mut msg: [u8; RAW_HEADER_MSG_LEN] = [0; RAW_HEADER_MSG_LEN];
write_stream_id(
&handshake_hash,
self.is_initiator,
&mut msg[..STREAM_ID_LENGTH],
);
let (tx, rx) = self.split_handshake();
let (header, pusher) = PushStream::init(OsRng, &Key::from(tx));
msg[STREAM_ID_LENGTH..].copy_from_slice(header.as_ref());
let Self {
is_initiator,
pattern,
state,
msg_buf,
local_public_key,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: EncryptorReady {
rx: Key::from(rx),
pusher,
handshake_hash,
},
},
msg.to_vec(),
))
}
}
impl SecStream<Initiator<IK, HsMsgSent>> {
pub fn read_msg(
mut self,
msg: &[u8],
) -> Result<(SecStream<Initiator<IK, HsDone>>, Vec<u8>), Error> {
let len = self.state.read_message(msg, &mut self.msg_buf)?;
let payload = &self.msg_buf[..len];
assert!(
self.state.is_handshake_finished(),
"IK handshake should be finished"
);
let Self {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Initiator {
_pattern: PhantomData,
_step: PhantomData,
},
},
payload.to_vec(),
))
}
pub fn read_and_write_msg(
self,
msg: &[u8],
) -> Result<(SecStream<EncryptorReady>, Vec<u8>), Error> {
let (self2, _payload) = self.read_msg(msg)?;
self2.write_msg()
}
}
impl SecStream<Initiator<XX, HsMsgSent>> {
pub fn read_msg(
mut self,
msg: &[u8],
) -> Result<(SecStream<Initiator<XX, InitiatorXxFinalMsg>>, Vec<u8>), Error> {
let len = self.state.read_message(msg, &mut self.msg_buf)?;
let payload = &self.msg_buf[..len];
assert!(
!self.state.is_handshake_finished(),
"XX handshake should not be finished yet"
);
let Self {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Initiator {
_pattern: PhantomData,
_step: PhantomData,
},
},
payload.to_vec(),
))
}
}
impl SecStream<Initiator<XX, InitiatorXxFinalMsg>> {
pub fn write_msg(mut self) -> Result<(SecStream<Initiator<XX, HsDone>>, Vec<u8>), Error> {
let len = self.state.write_message(&[], &mut self.msg_buf)?;
let msg = self.msg_buf[..len].to_vec();
assert!(
self.state.is_handshake_finished(),
"XX handshake should be finished after third message"
);
let Self {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Initiator {
_pattern: PhantomData,
_step: PhantomData,
},
},
msg,
))
}
}
impl SecStream<Initiator<IK, HsDone>> {
pub fn write_msg(mut self) -> Result<(SecStream<EncryptorReady>, Vec<u8>), Error> {
assert!(
self.state.is_handshake_finished(),
"Handshake must be finished before sending setup message"
);
let (tx, rx) = self.split_handshake();
let key: [u8; SNOW_CIPHERKEYLEN] = tx[..SNOW_CIPHERKEYLEN]
.try_into()
.expect("split_tx with incorrect length");
let key = Key::from(key);
let handshake_hash = self.state.get_handshake_hash().to_vec();
let (header, pusher) = PushStream::init(OsRng, &key);
let mut msg: [u8; RAW_HEADER_MSG_LEN] = [0; RAW_HEADER_MSG_LEN];
write_stream_id(
&handshake_hash,
self.is_initiator,
&mut msg[..STREAM_ID_LENGTH],
);
msg[STREAM_ID_LENGTH..].copy_from_slice(header.as_ref());
let SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: EncryptorReady {
pusher,
rx: Key::from(rx),
handshake_hash,
},
},
msg.to_vec(),
))
}
}
impl SecStream<Initiator<XX, HsDone>> {
pub fn write_msg(mut self) -> Result<(SecStream<EncryptorReady>, Vec<u8>), Error> {
assert!(
self.state.is_handshake_finished(),
"Handshake must be finished before sending setup message"
);
let (tx, rx) = self.split_handshake();
let key: [u8; SNOW_CIPHERKEYLEN] = tx[..SNOW_CIPHERKEYLEN]
.try_into()
.expect("split_tx with incorrect length");
let key = Key::from(key);
let handshake_hash = self.state.get_handshake_hash().to_vec();
let (header, pusher) = PushStream::init(OsRng, &key);
let mut msg: [u8; RAW_HEADER_MSG_LEN] = [0; RAW_HEADER_MSG_LEN];
write_stream_id(
&handshake_hash,
self.is_initiator,
&mut msg[..STREAM_ID_LENGTH],
);
msg[STREAM_ID_LENGTH..].copy_from_slice(header.as_ref());
let SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
..
} = self;
Ok((
SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: EncryptorReady {
pusher,
rx: Key::from(rx),
handshake_hash,
},
},
msg.to_vec(),
))
}
}
impl SecStream<EncryptorReady> {
pub fn handshake_hash(&self) -> &[u8] {
&self.step.handshake_hash
}
pub fn read_msg(self, msg: &[u8]) -> Result<SecStream<Ready>, Error> {
let Self {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step:
EncryptorReady {
pusher,
rx,
handshake_hash,
},
} = self;
let mut expected_stream_id: [u8; STREAM_ID_LENGTH] = [0; STREAM_ID_LENGTH];
write_stream_id(&handshake_hash, !is_initiator, &mut expected_stream_id);
if expected_stream_id != msg[..STREAM_ID_LENGTH] {
panic!(
"stream ID's don't match\n{expected_stream_id:?}\n != \n{:?}",
&msg[..STREAM_ID_LENGTH]
);
}
let header: [u8; Header::BYTES] =
msg[STREAM_ID_LENGTH..].try_into().expect("TODO wrong size");
let puller = PullStream::init(header.into(), &rx);
Ok(SecStream {
is_initiator,
pattern,
state,
local_public_key,
msg_buf,
step: Ready {
pusher,
puller,
handshake_hash,
},
})
}
pub fn push(
&mut self,
msg: &mut Vec<u8>,
associated_data: &[u8],
tag: Tag,
) -> Result<(), Error> {
Ok(self.step.pusher.push(msg, associated_data, tag)?)
}
}
impl SecStream<Ready> {
pub fn push(
&mut self,
msg: &mut Vec<u8>,
associated_data: &[u8],
tag: Tag,
) -> Result<(), Error> {
Ok(self.step.pusher.push(msg, associated_data, tag)?)
}
pub fn pull(&mut self, msg: &mut Vec<u8>, associated_data: &[u8]) -> Result<Tag, Error> {
Ok(self.step.puller.pull(msg, associated_data)?)
}
pub fn handshake_hash(&self) -> &[u8] {
&self.step.handshake_hash
}
}