use crate::{
error::parser::TunnelBuildRecordParseError,
i2np::{HopRole, AES256_IV_LEN, ROUTER_HASH_LEN},
primitives::{Mapping, MessageId, RouterId, TunnelId},
};
use bytes::{BufMut, BytesMut};
use nom::{
bytes::complete::take,
number::complete::{be_u32, be_u8},
Err, IResult,
};
use rand::CryptoRng;
use alloc::{vec, vec::Vec};
#[derive(Default)]
pub struct TunnelBuildReplyBuilder;
impl TunnelBuildReplyBuilder {
pub fn from_records(records: Vec<Vec<u8>>) -> Vec<u8> {
let mut out = BytesMut::with_capacity(1 + 218 * records.len());
out.put_u8(records.len() as u8);
records
.into_iter()
.fold(out, |mut acc, record| {
acc.put_slice(&record);
acc
})
.freeze()
.to_vec()
}
}
#[derive(Default)]
pub struct TunnelBuildRecordBuilder<'a> {
tunnel_id: Option<TunnelId>,
next_tunnel_id: Option<TunnelId>,
next_router_hash: Option<&'a [u8]>,
role: Option<HopRole>,
request_time: Option<u32>,
request_expiration: Option<u32>,
next_message_id: Option<MessageId>,
}
impl<'a> TunnelBuildRecordBuilder<'a> {
pub fn with_tunnel_id(mut self, tunnel_id: TunnelId) -> Self {
self.tunnel_id = Some(tunnel_id);
self
}
pub fn with_next_tunnel_id(mut self, next_tunnel_id: TunnelId) -> Self {
self.next_tunnel_id = Some(next_tunnel_id);
self
}
pub fn with_next_router_hash(mut self, next_router_hash: &'a [u8]) -> Self {
self.next_router_hash = Some(next_router_hash);
self
}
pub fn with_hop_role(mut self, role: HopRole) -> Self {
self.role = Some(role);
self
}
pub fn with_request_time(mut self, request_time: u32) -> Self {
self.request_time = Some(request_time);
self
}
pub fn with_request_expiration(mut self, request_expiration: u32) -> Self {
self.request_expiration = Some(request_expiration);
self
}
pub fn with_next_message_id(mut self, next_message_id: MessageId) -> Self {
self.next_message_id = Some(next_message_id);
self
}
pub fn random<R: CryptoRng>(rng: &mut R) -> Vec<u8> {
let mut out = vec![0u8; 218];
rng.fill_bytes(&mut out);
out
}
pub fn serialize(self, rng: &mut impl CryptoRng) -> Vec<u8> {
let mut out = BytesMut::with_capacity(154 + AES256_IV_LEN);
out.put_u32(*self.tunnel_id.expect("to exist"));
out.put_u32(*self.next_tunnel_id.expect("to exist"));
out.put_slice(self.next_router_hash.expect("to exist"));
out.put_u8(self.role.expect("to exist").as_u8());
out.put_u16(0u16); out.put_u8(0u8);
out.put_u32(self.request_time.expect("to exist"));
out.put_u32(self.request_expiration.expect("to exist"));
out.put_u32(*self.next_message_id.expect("to exist"));
out.put_u16(0u16);
let mut padding = vec![0u8; out.capacity() - out.len() - AES256_IV_LEN];
rng.fill_bytes(&mut padding);
out.put_slice(&padding);
out.freeze().to_vec()
}
}
#[derive(Debug)]
pub struct TunnelBuildRecord {
next_message_id: MessageId,
next_router: RouterId,
next_tunnel_id: TunnelId,
role: HopRole,
tunnel_id: TunnelId,
}
impl TunnelBuildRecord {
pub fn parse_frame(input: &[u8]) -> IResult<&[u8], Self, TunnelBuildRecordParseError> {
let (rest, tunnel_id) = be_u32(input)?;
let (rest, next_tunnel_id) = be_u32(rest)?;
let (rest, next_router_hash) = take(ROUTER_HASH_LEN)(rest)?;
let (rest, flags) = be_u8(rest)?;
let (rest, _reserved) = take(2usize)(rest)?;
let (rest, _encryption_type) = be_u8(rest)?;
let (rest, _request_time) = be_u32(rest)?;
let (rest, _request_expiration) = be_u32(rest)?;
let (rest, next_message_id) = be_u32(rest)?;
let (rest, _options) = Mapping::parse_frame(rest).map_err(Err::convert)?;
let (rest, _padding) = take(rest.len())(rest)?;
let role = HopRole::from_u8(flags)
.ok_or(Err::Error(TunnelBuildRecordParseError::InvalidHop(flags)))?;
Ok((
rest,
Self {
next_message_id: MessageId::from(next_message_id),
next_router: RouterId::from(next_router_hash),
next_tunnel_id: TunnelId::from(next_tunnel_id),
role,
tunnel_id: TunnelId::from(tunnel_id),
},
))
}
pub fn parse(input: &[u8]) -> Result<Self, TunnelBuildRecordParseError> {
Ok(Self::parse_frame(input)?.1)
}
pub fn tunnel_id(&self) -> TunnelId {
self.tunnel_id
}
pub fn next_tunnel_id(&self) -> TunnelId {
self.next_tunnel_id
}
pub fn next_router(&self) -> RouterId {
self.next_router.clone()
}
pub fn role(&self) -> HopRole {
self.role
}
pub fn next_message_id(&self) -> MessageId {
self.next_message_id
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::runtime::{mock::MockRuntime, Runtime};
#[test]
fn all_zero_bytes() {
let serialized = TunnelBuildRecordBuilder::default()
.with_tunnel_id(TunnelId::from(0))
.with_next_tunnel_id(TunnelId::from(0))
.with_next_router_hash(&[0u8; 32])
.with_hop_role(HopRole::Participant)
.with_request_time(0)
.with_request_expiration(0)
.with_next_message_id(MessageId::from(0))
.serialize(&mut MockRuntime::rng());
assert!(TunnelBuildRecord::parse(&serialized).is_ok());
}
#[test]
fn invalid_role() {
use rand::Rng;
let mut out = BytesMut::with_capacity(154 + AES256_IV_LEN);
out.put_u32(0u32);
out.put_u32(0u32);
out.put_slice(&[0u8; 32]);
out.put_u8(254);
out.put_u16(0u16); out.put_u8(0u8);
out.put_u32(0u32);
out.put_u32(0u32);
out.put_u32(0u32);
out.put_u16(0u16);
let mut padding = vec![0u8; out.capacity() - out.len() - AES256_IV_LEN];
MockRuntime::rng().fill_bytes(&mut padding);
out.put_slice(&padding);
let serialized = out.freeze().to_vec();
assert_eq!(
TunnelBuildRecord::parse(&serialized).unwrap_err(),
TunnelBuildRecordParseError::InvalidHop(254)
);
}
#[test]
fn options_parsed_correctly() {
use rand::Rng;
let mut out = BytesMut::with_capacity(154 + AES256_IV_LEN);
out.put_u32(0u32);
out.put_u32(0u32);
out.put_slice(&[0u8; 32]);
out.put_u8(HopRole::InboundGateway.as_u8());
out.put_u16(0u16); out.put_u8(0u8);
out.put_u32(0u32);
out.put_u32(0u32);
out.put_u32(0u32);
{
let mut option1 = Mapping::default();
option1.insert("hello".into(), "world".into());
let option1 = option1.serialize();
let mut option2 = Mapping::default();
option2.insert("goodbye".into(), "world".into());
let option2 = option2.serialize();
out.put_u16((option1.len() + option2.len()) as u16);
out.put_slice(&option1);
out.put_slice(&option2);
}
let mut padding = vec![0u8; out.capacity() - out.len() - AES256_IV_LEN];
MockRuntime::rng().fill_bytes(&mut padding);
out.put_slice(&padding);
let serialized = out.freeze().to_vec();
assert!(TunnelBuildRecord::parse(&serialized).is_ok());
}
}