use crate::{crypto::sha256::Sha256, error::parser::I2npParseError, runtime::Runtime};
use bytes::{BufMut, BytesMut};
use nom::{
bytes::complete::take,
number::complete::{be_u16, be_u32, be_u64, be_u8},
Err, IResult,
};
use alloc::vec::Vec;
use core::{fmt, time::Duration};
pub mod database;
pub mod delivery_status;
pub mod garlic;
pub mod tunnel;
const LOG_TARGET: &str = "emissary::i2np";
const ROUTER_HASH_LEN: usize = 32usize;
const AES256_KEY_LEN: usize = 32usize;
const AES256_IV_LEN: usize = 16usize;
const I2NP_SHORT_HEADER_LEN: usize = 9usize;
const I2NP_STANDARD_HEADER_LEN: usize = 16usize;
pub const I2NP_MESSAGE_EXPIRATION: Duration = Duration::from_millis(8000);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MessageType {
DatabaseStore,
DatabaseLookup,
DatabaseSearchReply,
DeliveryStatus,
Garlic,
TunnelData,
TunnelGateway,
Data,
TunnelBuild,
TunnelBuildReply,
VariableTunnelBuild,
VariableTunnelBuildReply,
ShortTunnelBuild,
OutboundTunnelBuildReply,
}
impl MessageType {
pub fn as_u8(&self) -> u8 {
match self {
Self::DatabaseStore => 1,
Self::DatabaseLookup => 2,
Self::DatabaseSearchReply => 3,
Self::DeliveryStatus => 10,
Self::Garlic => 11,
Self::TunnelData => 18,
Self::TunnelGateway => 19,
Self::Data => 20,
Self::TunnelBuild => 21,
Self::TunnelBuildReply => 22,
Self::VariableTunnelBuild => 23,
Self::VariableTunnelBuildReply => 24,
Self::ShortTunnelBuild => 25,
Self::OutboundTunnelBuildReply => 26,
}
}
pub fn from_u8(msg_type: u8) -> Option<MessageType> {
match msg_type {
1 => Some(Self::DatabaseStore),
2 => Some(Self::DatabaseLookup),
3 => Some(Self::DatabaseSearchReply),
10 => Some(Self::DeliveryStatus),
11 => Some(Self::Garlic),
18 => Some(Self::TunnelData),
19 => Some(Self::TunnelGateway),
20 => Some(Self::Data),
21 => Some(Self::TunnelBuild),
22 => Some(Self::TunnelBuildReply),
23 => Some(Self::VariableTunnelBuild),
24 => Some(Self::VariableTunnelBuildReply),
25 => Some(Self::ShortTunnelBuild),
26 => Some(Self::OutboundTunnelBuildReply),
msg_type => {
tracing::warn!(
target: LOG_TARGET,
?msg_type,
"invalid message id",
);
None
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HopRole {
InboundGateway,
OutboundEndpoint,
Participant,
}
impl HopRole {
fn as_u8(self) -> u8 {
match self {
HopRole::InboundGateway => 0x80,
HopRole::OutboundEndpoint => 0x40,
HopRole::Participant => 0x00,
}
}
fn from_u8(role: u8) -> Option<HopRole> {
match role {
0x80 => Some(HopRole::InboundGateway),
0x40 => Some(HopRole::OutboundEndpoint),
0x00 => Some(HopRole::Participant),
role => {
tracing::warn!(
target: LOG_TARGET,
?role,
"unrecognized flag"
);
None
}
}
}
}
#[derive(Debug)]
pub enum MessageBuilder<'a> {
Standard {
message_type: Option<MessageType>,
message_id: Option<u32>,
expiration: Option<Duration>,
payload: Option<&'a [u8]>,
},
Short {
message_type: Option<MessageType>,
message_id: Option<u32>,
expiration: Option<Duration>,
payload: Option<&'a [u8]>,
},
}
impl<'a> MessageBuilder<'a> {
pub fn short() -> Self {
Self::Short {
message_type: None,
message_id: None,
expiration: None,
payload: None,
}
}
pub fn standard() -> Self {
Self::Standard {
message_type: None,
message_id: None,
expiration: None,
payload: None,
}
}
pub fn with_expiration(mut self, message_expiration: Duration) -> Self {
match self {
Self::Standard {
expiration: ref mut exp,
..
}
| Self::Short {
expiration: ref mut exp,
..
} => *exp = Some(message_expiration),
}
self
}
pub fn with_message_type(mut self, message_type: MessageType) -> Self {
match self {
Self::Standard {
message_type: ref mut msg_type,
..
}
| Self::Short {
message_type: ref mut msg_type,
..
} => *msg_type = Some(message_type),
}
self
}
pub fn with_message_id<T: Into<u32>>(mut self, message_id: T) -> Self {
match self {
Self::Standard {
message_id: ref mut msg_id,
..
}
| Self::Short {
message_id: ref mut msg_id,
..
} => *msg_id = Some(message_id.into()),
}
self
}
pub fn with_payload(mut self, payload: &'a [u8]) -> Self {
match self {
Self::Standard {
payload: ref mut msg_payload,
..
}
| Self::Short {
payload: ref mut msg_payload,
..
} => *msg_payload = Some(payload),
}
self
}
pub fn build(self) -> Vec<u8> {
match self {
Self::Standard {
message_type,
message_id,
expiration,
mut payload,
} => {
let payload = payload.take().expect("to exist");
let mut out = BytesMut::with_capacity(payload.len() + I2NP_STANDARD_HEADER_LEN);
out.put_u8(message_type.expect("to exist").as_u8());
out.put_u32(message_id.expect("to exist"));
out.put_u64(expiration.expect("to exist").as_millis() as u64);
out.put_u16(payload.len() as u16);
out.put_u8(Sha256::new().update(payload).finalize()[0]); out.put_slice(payload);
out.freeze().to_vec()
}
Self::Short {
message_type,
message_id,
expiration,
mut payload,
} => {
let payload = payload.take().expect("to exist");
let mut out = BytesMut::with_capacity(payload.len() + I2NP_SHORT_HEADER_LEN + 2);
out.put_u16((payload.len() + I2NP_SHORT_HEADER_LEN) as u16);
out.put_u8(message_type.expect("to exist").as_u8());
out.put_u32(message_id.expect("to exist"));
out.put_u32(expiration.expect("to exist").as_secs() as u32);
out.put_slice(payload);
out.freeze().to_vec()
}
}
}
}
#[derive(Clone)]
pub struct Message {
pub message_type: MessageType,
pub message_id: u32,
pub expiration: Duration,
pub payload: Vec<u8>,
}
impl Default for Message {
fn default() -> Self {
Self {
message_type: MessageType::Data,
message_id: 0u32,
expiration: Duration::new(0u64, 0u32),
payload: Vec::new(),
}
}
}
impl fmt::Debug for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Message")
.field("message_type", &self.message_type)
.field("message_id", &self.message_id)
.field("expiration", &self.expiration)
.finish_non_exhaustive()
}
}
impl Message {
pub fn parse_frame_short(input: &[u8]) -> IResult<&[u8], Message, I2npParseError> {
let (rest, size) = be_u16(input)?;
let (rest, message_type) = be_u8(rest)?;
let (rest, message_id) = be_u32(rest)?;
let (rest, expiration) = be_u32(rest)?;
if size as usize <= I2NP_SHORT_HEADER_LEN {
return Err(Err::Error(I2npParseError::TooShortHeader));
}
let (rest, payload) = take(size as usize - I2NP_SHORT_HEADER_LEN)(rest)?;
let message_type = MessageType::from_u8(message_type)
.ok_or(Err::Error(I2npParseError::InvalidMessage(message_type)))?;
Ok((
rest,
Message {
message_type,
message_id,
expiration: Duration::from_secs(expiration as u64),
payload: payload.to_vec(),
},
))
}
pub fn parse_frame_standard(input: &[u8]) -> IResult<&[u8], Message, I2npParseError> {
let (rest, message_type) = be_u8(input)?;
let (rest, message_id) = be_u32(rest)?;
let (rest, expiration) = be_u64(rest)?;
let (rest, size) = be_u16(rest)?;
let (rest, _checksum) = be_u8(rest)?;
let (rest, payload) = take(size as usize)(rest)?;
if payload.is_empty() {
return Err(Err::Error(I2npParseError::EmptyPayload));
}
let message_type = MessageType::from_u8(message_type)
.ok_or(Err::Error(I2npParseError::InvalidMessage(message_type)))?;
Ok((
rest,
Message {
message_type,
message_id,
expiration: Duration::from_millis(expiration),
payload: payload.to_vec(),
},
))
}
pub fn parse_short(input: &[u8]) -> Result<Message, I2npParseError> {
Ok(Self::parse_frame_short(input)?.1)
}
pub fn parse_standard(input: &[u8]) -> Result<Message, I2npParseError> {
Ok(Self::parse_frame_standard(input)?.1)
}
pub fn serialize_short(self) -> Vec<u8> {
MessageBuilder::short()
.with_expiration(self.expiration)
.with_message_type(self.message_type)
.with_message_id(self.message_id)
.with_payload(&self.payload)
.build()
}
pub fn serialize_standard(self) -> Vec<u8> {
MessageBuilder::standard()
.with_expiration(self.expiration)
.with_message_type(self.message_type)
.with_message_id(self.message_id)
.with_payload(&self.payload)
.build()
}
pub fn serialized_len_short(&self) -> usize {
self.payload.len() + I2NP_SHORT_HEADER_LEN + 2 }
pub fn serialized_len_long(&self) -> usize {
self.payload.len() + I2NP_STANDARD_HEADER_LEN
}
pub fn is_expired<R: Runtime>(&self) -> bool {
self.expiration < R::time_since_epoch()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::runtime::mock::MockRuntime;
#[test]
fn parse_short_as_standard() {
let message = MessageBuilder::short()
.with_message_type(MessageType::DeliveryStatus)
.with_message_id(1337u32)
.with_expiration(Duration::from_secs(0xdeadbeefu64))
.with_payload(&vec![1, 2, 3, 4])
.build();
assert_eq!(
Message::parse_standard(&message).unwrap_err(),
I2npParseError::InvalidBitstream
);
}
#[test]
fn parse_standard_as_short() {
let message = MessageBuilder::standard()
.with_message_type(MessageType::DeliveryStatus)
.with_message_id(1337u32)
.with_expiration(Duration::from_secs(0xdeadbeefu64))
.with_payload(&vec![1, 2, 3, 4])
.build();
assert_eq!(
Message::parse_short(&message).unwrap_err(),
I2npParseError::InvalidBitstream
);
}
#[test]
fn invalid_message_type() {
let mut out = BytesMut::with_capacity(4 + I2NP_SHORT_HEADER_LEN + 2);
out.put_u16((4 + I2NP_SHORT_HEADER_LEN) as u16);
out.put_u8(252);
out.put_u32(13371338u32);
out.put_u32(0xdeadbeefu32);
out.put_slice(&vec![1, 2, 3, 4]);
let serialized = out.freeze().to_vec();
assert_eq!(
Message::parse_short(&serialized).unwrap_err(),
I2npParseError::InvalidMessage(252)
);
}
#[test]
fn incomplete_short_header() {
assert_eq!(
Message::parse_short(&vec![1, 2, 3, 4]).unwrap_err(),
I2npParseError::InvalidBitstream
);
}
#[test]
fn incomplete_standard_header() {
assert_eq!(
Message::parse_standard(&vec![1, 2, 3, 4]).unwrap_err(),
I2npParseError::InvalidBitstream
);
}
#[test]
fn invalid_size_short() {
let mut out = BytesMut::with_capacity(4 + I2NP_SHORT_HEADER_LEN + 2);
out.put_u16(4u16); out.put_u8(MessageType::DeliveryStatus.as_u8());
out.put_u32(13371338u32);
out.put_u32(0xdeadbeefu32);
out.put_slice(&vec![1, 2, 3, 4]);
let serialized = out.freeze().to_vec();
assert_eq!(
Message::parse_short(&serialized).unwrap_err(),
I2npParseError::TooShortHeader
);
}
#[test]
fn empty_payload_short() {
let mut out = BytesMut::with_capacity(I2NP_SHORT_HEADER_LEN + 2);
out.put_u16(4u16); out.put_u8(MessageType::DeliveryStatus.as_u8());
out.put_u32(13371338u32);
out.put_u32(0xdeadbeefu32);
let serialized = out.freeze().to_vec();
assert_eq!(
Message::parse_short(&serialized).unwrap_err(),
I2npParseError::TooShortHeader
);
}
#[test]
fn empty_payload_standard() {
let mut out = BytesMut::with_capacity(I2NP_SHORT_HEADER_LEN + 2);
out.put_u16(4u16); out.put_u8(MessageType::DeliveryStatus.as_u8());
out.put_u32(13371338u32);
out.put_u32(0xdeadbeefu32);
let serialized = out.freeze().to_vec();
assert_eq!(
Message::parse_short(&serialized).unwrap_err(),
I2npParseError::TooShortHeader
);
}
#[test]
fn i2np_message_expired_short() {
let message = MessageBuilder::short()
.with_message_type(MessageType::DeliveryStatus)
.with_message_id(1337u32)
.with_expiration(MockRuntime::time_since_epoch() - Duration::from_secs(10))
.with_payload(&vec![1, 2, 3, 4])
.build();
assert!(Message::parse_short(&message).unwrap().is_expired::<MockRuntime>());
}
#[test]
fn i2np_message_expired_standard() {
let message = MessageBuilder::standard()
.with_message_type(MessageType::DeliveryStatus)
.with_message_id(1337u32)
.with_expiration(MockRuntime::time_since_epoch() - I2NP_MESSAGE_EXPIRATION)
.with_payload(&vec![1, 2, 3, 4])
.build();
assert!(Message::parse_standard(&message).unwrap().is_expired::<MockRuntime>());
}
}