use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use ring::aead::{Aad, CHACHA20_POLY1305, LessSafeKey, Nonce, UnboundKey};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Presence {
pub endpoints: Vec<SocketAddr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Call {
pub nonce: u64,
pub endpoints: Vec<SocketAddr>,
}
impl Presence {
pub fn open(key: &[u8; 32], sealed: &[u8]) -> Option<Presence> {
let (endpoints, rest) = decode(&open(key, sealed)?)?;
rest.is_empty().then_some(Presence { endpoints })
}
#[cfg(test)]
pub(crate) fn seal(&self, key: &[u8; 32]) -> Result<Vec<u8>, String> {
seal(key, &encode(&self.endpoints))
}
}
impl Call {
pub fn seal(&self, key: &[u8; 32]) -> Result<Vec<u8>, String> {
let mut plain = self.nonce.to_be_bytes().to_vec();
plain.extend(encode(&self.endpoints));
seal(key, &plain)
}
#[cfg(test)]
pub(crate) fn open(key: &[u8; 32], sealed: &[u8]) -> Option<Call> {
let plain = open(key, sealed)?;
let (head, tail) = plain.split_at_checked(8)?;
let nonce = u64::from_be_bytes(<[u8; 8]>::try_from(head).ok()?);
let (endpoints, rest) = decode(tail)?;
rest.is_empty().then_some(Call { nonce, endpoints })
}
}
fn encode(endpoints: &[SocketAddr]) -> Vec<u8> {
let mut out = vec![u8::try_from(endpoints.len()).unwrap_or(u8::MAX)];
for addr in endpoints.iter().take(255) {
match addr.ip() {
IpAddr::V4(ip) => {
out.push(4);
out.extend_from_slice(&ip.octets());
}
IpAddr::V6(ip) => {
out.push(6);
out.extend_from_slice(&ip.octets());
}
}
out.extend_from_slice(&addr.port().to_be_bytes());
}
out
}
fn decode(bytes: &[u8]) -> Option<(Vec<SocketAddr>, Vec<u8>)> {
let (count, mut rest) = bytes.split_first()?;
let mut endpoints = Vec::new();
for _ in 0..*count {
let (family, tail) = rest.split_first()?;
let width = match family {
4 => 4,
6 => 16,
_ => return None,
};
let (ip, tail) = tail.split_at_checked(width)?;
let (port, tail) = tail.split_at_checked(2)?;
let ip = match family {
4 => IpAddr::V4(Ipv4Addr::from(<[u8; 4]>::try_from(ip).ok()?)),
_ => IpAddr::V6(Ipv6Addr::from(<[u8; 16]>::try_from(ip).ok()?)),
};
let port = u16::from_be_bytes(<[u8; 2]>::try_from(port).ok()?);
endpoints.push(SocketAddr::new(ip, port));
rest = tail;
}
Some((endpoints, rest.to_vec()))
}
fn seal(key: &[u8; 32], plain: &[u8]) -> Result<Vec<u8>, String> {
let mut nonce = [0u8; 12];
crate::dht::random(&mut nonce)?;
let key = aead(key)?;
let mut body = plain.to_vec();
key.seal_in_place_append_tag(Nonce::assume_unique_for_key(nonce), Aad::empty(), &mut body)
.map_err(|_| "the seal refused".to_owned())?;
let mut out = nonce.to_vec();
out.extend(body);
Ok(out)
}
fn open(key: &[u8; 32], sealed: &[u8]) -> Option<Vec<u8>> {
let (nonce, body) = sealed.split_at_checked(12)?;
let nonce = Nonce::try_assume_unique_for_key(nonce).ok()?;
let mut body = body.to_vec();
let plain = aead(key)
.ok()?
.open_in_place(nonce, Aad::empty(), &mut body)
.ok()?;
Some(plain.to_vec())
}
fn aead(key: &[u8; 32]) -> Result<LessSafeKey, String> {
UnboundKey::new(&CHACHA20_POLY1305, key)
.map(LessSafeKey::new)
.map_err(|_| "the seal key is not 32 bytes".to_owned())
}
#[cfg(test)]
mod tests;