use std::borrow::Cow;
use std::marker::PhantomData;
use std::net::IpAddr;
use super::mac::{ClusterMac, Mac};
use super::{Authenticated, Authenticator, Payload, Verified, VERSION_LEN};
use crate::replay::{ReplayFilter, Seq, Stamp, REPLAY_HEADER_LEN};
fn decode_replay_header(data: &[u8]) -> Option<(Seq, Stamp, &[u8])> {
if data.len() < REPLAY_HEADER_LEN {
return None;
}
let seq = Seq::from_le_bytes(data[..8].try_into().unwrap());
let stamp = Stamp::from_le_bytes(data[8..16].try_into().unwrap());
Some((seq, stamp, &data[REPLAY_HEADER_LEN..]))
}
impl<'a> Payload<'a, Authenticated> {
pub fn check_version(self) -> Result<Self, u8> {
let version = *self.bytes.first().unwrap_or(&0);
if version != super::WIRE_VERSION {
return Err(version);
}
let bytes = match self.bytes {
Cow::Borrowed(b) => Cow::Borrowed(&b[VERSION_LEN..]),
Cow::Owned(mut b) => {
b.drain(..VERSION_LEN);
Cow::Owned(b)
}
};
Ok(Payload {
bytes,
seq: self.seq,
stamp: self.stamp,
_state: PhantomData,
})
}
pub fn verify_replay(
self,
filter: &ReplayFilter,
sender: IpAddr,
) -> Option<Payload<'a, Verified>> {
if !filter.check_and_record(sender, self.seq, self.stamp) {
return None;
}
Some(Payload {
bytes: self.bytes,
seq: self.seq,
stamp: self.stamp,
_state: PhantomData,
})
}
}
impl Payload<'_, Verified> {
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
impl Authenticator {
pub fn open<'a>(&self, datagram: &'a [u8]) -> Option<Payload<'a, Authenticated>> {
match self {
Authenticator::Disabled => Some(Payload {
bytes: Cow::Borrowed(datagram),
seq: Seq::NONE,
stamp: Stamp::NONE,
_state: PhantomData,
}),
Authenticator::Enabled(keys) => {
if datagram.len() < super::TAG_LEN + REPLAY_HEADER_LEN {
return None;
}
let (tag, protected) = datagram.split_at(super::TAG_LEN);
if !keys
.iter()
.any(|key| ClusterMac::verify(key, protected, tag))
{
return None;
}
let (seq, stamp, messages) = decode_replay_header(protected)?;
Some(Payload {
bytes: Cow::Borrowed(messages),
seq,
stamp,
_state: PhantomData,
})
}
#[cfg(feature = "encryption")]
Authenticator::Encrypted(keys) => {
let plaintext = keys
.iter()
.find_map(|key| super::encryption::open(key, datagram))?;
let (seq, stamp, messages) = decode_replay_header(&plaintext)?;
Some(Payload {
bytes: Cow::Owned(messages.to_vec()),
seq,
stamp,
_state: PhantomData,
})
}
}
}
}