use crate::{
crypto::StaticPublicKey,
error::parser::GarlicParseError,
i2np::{Message, MessageType, LOG_TARGET},
primitives::MessageId,
};
use bytes::{BufMut, BytesMut};
use nom::{
bytes::complete::take,
number::complete::{be_u16, be_u32, be_u8},
Err, IResult,
};
use alloc::{boxed::Box, vec::Vec};
use core::{fmt, time::Duration};
const GARLIC_HEADER_LEN: usize = 3;
pub const GARLIC_MESSAGE_OVERHEAD: usize = 16 + 32 + 4;
#[derive(Debug)]
pub enum GarlicMessageType {
DateTime,
Termination,
Options,
MessageNumber,
NextKey,
Ack,
AckRequest,
GarlicClove,
Padding,
}
impl GarlicMessageType {
fn from_u8(byte: u8) -> Option<Self> {
match byte {
0 => Some(Self::DateTime),
4 => Some(Self::Termination),
5 => Some(Self::Options),
6 => Some(Self::MessageNumber),
7 => Some(Self::NextKey),
8 => Some(Self::Ack),
9 => Some(Self::AckRequest),
11 => Some(Self::GarlicClove),
254 => Some(Self::Padding),
_ => None,
}
}
fn as_u8(self) -> u8 {
match self {
Self::DateTime => 0,
Self::Termination => 4,
Self::Options => 5,
Self::MessageNumber => 6,
Self::NextKey => 7,
Self::Ack => 8,
Self::AckRequest => 9,
Self::GarlicClove => 11,
Self::Padding => 254,
}
}
}
#[derive(Debug)]
pub enum DeliveryInstructions<'a> {
Local,
Destination {
hash: &'a [u8],
},
Router {
hash: &'a [u8],
},
Tunnel {
hash: &'a [u8],
tunnel_id: u32,
},
}
#[derive(Debug)]
pub enum OwnedDeliveryInstructions {
Local,
Destination {
hash: Vec<u8>,
},
Router {
hash: Vec<u8>,
},
Tunnel {
hash: Vec<u8>,
tunnel_id: u32,
},
}
impl<'a> From<&'a DeliveryInstructions<'a>> for OwnedDeliveryInstructions {
fn from(value: &'a DeliveryInstructions<'a>) -> Self {
match value {
DeliveryInstructions::Local => OwnedDeliveryInstructions::Local,
DeliveryInstructions::Destination { hash } => OwnedDeliveryInstructions::Destination {
hash: hash.to_vec(),
},
DeliveryInstructions::Router { hash } => OwnedDeliveryInstructions::Router {
hash: hash.to_vec(),
},
DeliveryInstructions::Tunnel { hash, tunnel_id } => OwnedDeliveryInstructions::Tunnel {
hash: hash.to_vec(),
tunnel_id: *tunnel_id,
},
}
}
}
impl DeliveryInstructions<'_> {
fn serialized_len(&self) -> usize {
match self {
Self::Local => 1usize,
Self::Destination { .. } | Self::Router { .. } => 33usize,
Self::Tunnel { .. } => 37usize,
}
}
fn serialize(self) -> BytesMut {
let mut out = BytesMut::with_capacity(self.serialized_len());
match self {
Self::Local => out.put_u8(0x00),
Self::Destination { hash } => {
out.put_u8(0x01 << 5);
out.put_slice(hash);
}
Self::Router { hash } => {
out.put_u8(0x02 << 5);
out.put_slice(hash);
}
Self::Tunnel { hash, tunnel_id } => {
out.put_u8(0x03 << 5);
out.put_slice(hash);
out.put_u32(tunnel_id);
}
}
out
}
}
pub struct NextKeyBuilder {
forward: bool,
public_key: Option<StaticPublicKey>,
key_id: u16,
request_reverse_key: bool,
}
impl NextKeyBuilder {
pub fn forward(key_id: u16) -> Self {
Self {
forward: true,
key_id,
public_key: None,
request_reverse_key: false,
}
}
pub fn reverse(key_id: u16) -> Self {
Self {
forward: false,
key_id,
public_key: None,
request_reverse_key: false,
}
}
pub fn with_public_key(mut self, public_key: StaticPublicKey) -> Self {
self.public_key = Some(public_key);
self
}
pub fn with_request_reverse_key(mut self, request_reverse_key: bool) -> Self {
self.request_reverse_key = request_reverse_key;
self
}
pub fn build(self) -> NextKeyKind {
match self.forward {
true => NextKeyKind::ForwardKey {
key_id: self.key_id,
public_key: self.public_key,
reverse_key_requested: self.request_reverse_key,
},
false => NextKeyKind::ReverseKey {
key_id: self.key_id,
public_key: self.public_key,
},
}
}
}
pub enum NextKeyKind {
ForwardKey {
key_id: u16,
public_key: Option<StaticPublicKey>,
reverse_key_requested: bool,
},
ReverseKey {
key_id: u16,
public_key: Option<StaticPublicKey>,
},
}
impl NextKeyKind {
fn serialized_len(&self) -> usize {
match self {
NextKeyKind::ForwardKey { public_key, .. }
| NextKeyKind::ReverseKey { public_key, .. }
if public_key.is_some() =>
GARLIC_HEADER_LEN + 3usize + 32usize, _ => GARLIC_HEADER_LEN + 3usize, }
}
}
impl fmt::Debug for NextKeyKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ForwardKey {
key_id,
public_key,
reverse_key_requested,
} => f
.debug_struct("NextKeyKind::ForwardKey")
.field("key_id", &key_id)
.field("key_exists", &public_key.is_some())
.field("reverse_requested", &reverse_key_requested)
.finish(),
Self::ReverseKey { key_id, public_key } => f
.debug_struct("NextKeyKind::ReverseKey")
.field("key_id", &key_id)
.field("key_exists", &public_key.is_some())
.finish(),
}
}
}
pub enum GarlicMessageBlock<'a> {
DateTime {
timestamp: u32,
},
Termination {},
Options {},
MessageNumber {},
NextKey {
kind: Box<NextKeyKind>,
},
Ack {
acks: Vec<(u16, u16)>,
},
AckRequest,
GarlicClove {
message_type: MessageType,
message_id: MessageId,
expiration: Duration,
delivery_instructions: DeliveryInstructions<'a>,
message_body: &'a [u8],
},
Padding {
padding: &'a [u8],
},
}
impl fmt::Debug for GarlicMessageBlock<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DateTime { timestamp } => f
.debug_struct("GarlicMessageBlock::DateTime")
.field("timestamp", ×tamp)
.finish(),
Self::GarlicClove {
message_type,
message_id,
expiration,
delivery_instructions,
..
} => f
.debug_struct("GarlicMessageBlock::GarlicClove")
.field("message_type", &message_type)
.field("message_id", &message_id)
.field("expiration", &expiration)
.field("delivery_instructions", &delivery_instructions)
.finish_non_exhaustive(),
Self::Padding { .. } =>
f.debug_struct("GarlicMessageBlock::Padding").finish_non_exhaustive(),
Self::Termination {} => f.debug_struct("GarlicMessageBlock::Termination").finish(),
Self::Options {} => f.debug_struct("GarlicMessageBlock::Options").finish(),
Self::MessageNumber {} => f.debug_struct("GarlicMessageBlock::MessageNumber").finish(),
Self::NextKey { kind } =>
f.debug_struct("GarlicMessageBlock::NextKey").field("kind", &kind).finish(),
Self::Ack { acks } => f
.debug_struct("GarlicMessageBlock::Ack")
.field("num_acks", &acks.len())
.finish(),
Self::AckRequest => f.debug_struct("GarlicMessageBlock::AckRequest").finish(),
}
}
}
#[derive(Debug)]
pub struct GarlicMessage<'a> {
pub blocks: Vec<GarlicMessageBlock<'a>>,
}
impl<'a> GarlicMessage<'a> {
fn parse_date_time(
input: &'a [u8],
) -> IResult<&'a [u8], GarlicMessageBlock<'a>, GarlicParseError> {
let (rest, _size) = be_u16(input)?;
let (rest, timestamp) = be_u32(rest)?;
Ok((rest, GarlicMessageBlock::DateTime { timestamp }))
}
fn parse_delivery_instructions(
input: &'a [u8],
) -> IResult<&'a [u8], DeliveryInstructions<'a>, GarlicParseError> {
let (rest, flag) = be_u8(input)?;
if (flag >> 7) & 1 != 0 {
return Err(Err::Error(GarlicParseError::EncryptionNotSupported));
}
if (flag >> 4) & 1 != 0 {
return Err(Err::Error(GarlicParseError::DelayNotSupported));
}
match (flag >> 5) & 0x3 {
0x00 => Ok((rest, DeliveryInstructions::Local)),
0x01 => {
let (rest, hash) = take(32usize)(rest)?;
Ok((rest, DeliveryInstructions::Destination { hash }))
}
0x02 => {
let (rest, hash) = take(32usize)(rest)?;
Ok((rest, DeliveryInstructions::Router { hash }))
}
0x03 => {
let (rest, hash) = take(32usize)(rest)?;
let (rest, tunnel_id) = be_u32(rest)?;
Ok((rest, DeliveryInstructions::Tunnel { hash, tunnel_id }))
}
kind => Err(Err::Error(GarlicParseError::InvalidDelivery(kind))),
}
}
fn parse_garlic_clove(
input: &'a [u8],
) -> IResult<&'a [u8], GarlicMessageBlock<'a>, GarlicParseError> {
let (rest, size) = be_u16(input)?;
let (rest, delivery_instructions) = Self::parse_delivery_instructions(rest)?;
let (rest, message_type) = be_u8(rest)?;
let (rest, message_id) = be_u32(rest)?;
let (rest, expiration) = be_u32(rest)?;
let message_type = MessageType::from_u8(message_type)
.ok_or(Err::Error(GarlicParseError::InvalidMessage(message_type)))?;
let message_body_len =
(size as usize).saturating_sub(delivery_instructions.serialized_len() + 1 + 2 * 4);
let (rest, message_body) = take(message_body_len)(rest)?;
Ok((
rest,
GarlicMessageBlock::GarlicClove {
message_type,
message_id: MessageId::from(message_id),
expiration: Duration::from_secs(expiration as u64),
delivery_instructions,
message_body,
},
))
}
fn parse_padding(
input: &'a [u8],
) -> IResult<&'a [u8], GarlicMessageBlock<'a>, GarlicParseError> {
let (rest, size) = be_u16(input)?;
let (rest, padding) = take(size)(rest)?;
Ok((rest, GarlicMessageBlock::Padding { padding }))
}
fn parse_next_key(
input: &'a [u8],
) -> IResult<&'a [u8], GarlicMessageBlock<'a>, GarlicParseError> {
let (rest, _size) = be_u16(input)?;
let (rest, flag) = be_u8(rest)?;
let (rest, key_id) = be_u16(rest)?;
let (rest, public_key) = match flag & 1 {
0 => (rest, None),
1 => {
let (rest, key) = take(32usize)(rest)?;
(
rest,
Some(StaticPublicKey::from_bytes(
TryInto::<[u8; 32]>::try_into(key).expect("to succeed"),
)),
)
}
_ => unreachable!(),
};
let kind = match (flag >> 1) & 1 {
1 => NextKeyKind::ReverseKey { key_id, public_key },
0 => NextKeyKind::ForwardKey {
key_id,
public_key,
reverse_key_requested: (flag >> 2) & 1 == 1,
},
_ => unreachable!(),
};
Ok((
rest,
GarlicMessageBlock::NextKey {
kind: Box::new(kind),
},
))
}
fn parse_ack_request(
input: &'a [u8],
) -> IResult<&'a [u8], GarlicMessageBlock<'a>, GarlicParseError> {
let (rest, _size) = be_u16(input)?;
let (rest, _flag) = be_u8(rest)?;
Ok((rest, GarlicMessageBlock::AckRequest))
}
fn parse_ack(input: &'a [u8]) -> IResult<&'a [u8], GarlicMessageBlock<'a>, GarlicParseError> {
let (rest, size) = be_u16(input)?;
if size % 4 != 0 {
return Err(Err::Error(GarlicParseError::InvalidSize));
}
let (rest, acks) = (0..size / 4)
.try_fold((rest, Vec::<(u16, u16)>::new()), |(rest, mut acks), _| {
let (rest, tag_set_id) = be_u16::<_, ()>(rest).ok()?;
let (rest, tag_index) = be_u16::<_, ()>(rest).ok()?;
acks.push((tag_set_id, tag_index));
Some((rest, acks))
})
.ok_or(Err::Error(GarlicParseError::InvalidAcks))?;
Ok((rest, GarlicMessageBlock::Ack { acks }))
}
fn parse_frame(input: &'a [u8]) -> IResult<&'a [u8], GarlicMessageBlock<'a>, GarlicParseError> {
let (rest, message_type) = be_u8(input)?;
match GarlicMessageType::from_u8(message_type) {
Some(GarlicMessageType::DateTime) => Self::parse_date_time(rest),
Some(GarlicMessageType::GarlicClove) => Self::parse_garlic_clove(rest),
Some(GarlicMessageType::Padding) => Self::parse_padding(rest),
Some(GarlicMessageType::NextKey) => Self::parse_next_key(rest),
Some(GarlicMessageType::AckRequest) => Self::parse_ack_request(rest),
Some(GarlicMessageType::Ack) => Self::parse_ack(rest),
parsed_message_type => {
tracing::warn!(
target: LOG_TARGET,
?message_type,
?parsed_message_type,
"unsupported garlic message block",
);
let (rest, size) = be_u16(rest)?;
let (rest, _) = take(size)(rest)?;
Self::parse_frame(rest)
}
}
}
fn parse_inner(
input: &'a [u8],
mut messages: Vec<GarlicMessageBlock<'a>>,
) -> Result<Vec<GarlicMessageBlock<'a>>, GarlicParseError> {
let (rest, message) = Self::parse_frame(input)?;
messages.push(message);
match rest.is_empty() {
true => Ok(messages),
false => Self::parse_inner(rest, messages),
}
}
pub fn parse(input: &'a [u8]) -> Result<Self, GarlicParseError> {
Ok(Self {
blocks: Self::parse_inner(input, Vec::new())?,
})
}
pub fn garlic_tag(message: &Message) -> u64 {
u64::from_le_bytes(
TryInto::<[u8; 8]>::try_into(&message.payload[4..12]).expect("valid garlic message"),
)
}
}
#[derive(Default)]
pub struct GarlicMessageBuilder<'a> {
cloves: Vec<GarlicMessageBlock<'a>>,
message_size: usize,
}
impl<'a> GarlicMessageBuilder<'a> {
pub fn with_date_time(mut self, timestamp: u32) -> Self {
self.message_size = self.message_size.saturating_add(GARLIC_HEADER_LEN).saturating_add(4); self.cloves.push(GarlicMessageBlock::DateTime { timestamp });
self
}
pub fn with_garlic_clove(
mut self,
message_type: MessageType,
message_id: MessageId,
expiration: Duration,
delivery_instructions: DeliveryInstructions<'a>,
message_body: &'a [u8],
) -> Self {
self.message_size = self
.message_size
.saturating_add(GARLIC_HEADER_LEN)
.saturating_add(delivery_instructions.serialized_len())
.saturating_add(1) .saturating_add(4) .saturating_add(4) .saturating_add(message_body.len());
self.cloves.push(GarlicMessageBlock::GarlicClove {
message_type,
message_id,
expiration,
delivery_instructions,
message_body,
});
self
}
pub fn with_next_key(mut self, kind: NextKeyKind) -> Self {
self.message_size += kind.serialized_len();
self.cloves.push(GarlicMessageBlock::NextKey {
kind: Box::new(kind),
});
self
}
pub fn with_ack_request(mut self) -> Self {
self.message_size += GARLIC_HEADER_LEN + 1; self.cloves.push(GarlicMessageBlock::AckRequest);
self
}
pub fn with_ack(mut self, acks: Vec<(u16, u16)>) -> Self {
self.message_size += GARLIC_HEADER_LEN + acks.len() * 4;
self.cloves.push(GarlicMessageBlock::Ack { acks });
self
}
pub fn build(self) -> Vec<u8> {
let mut out = BytesMut::with_capacity(self.message_size);
for clove in self.cloves {
match clove {
GarlicMessageBlock::DateTime { timestamp } => {
out.put_u8(GarlicMessageType::DateTime.as_u8());
out.put_u16(4u16); out.put_u32(timestamp);
}
GarlicMessageBlock::GarlicClove {
message_type,
message_id,
expiration,
delivery_instructions,
message_body,
} => {
out.put_u8(GarlicMessageType::GarlicClove.as_u8());
out.put_u16(
delivery_instructions
.serialized_len()
.saturating_add(1) .saturating_add(4) .saturating_add(4) .saturating_add(message_body.len()) as u16,
);
out.put_slice(&delivery_instructions.serialize());
out.put_u8(message_type.as_u8());
out.put_u32(*message_id);
out.put_u32(expiration.as_secs() as u32);
out.put_slice(message_body);
}
GarlicMessageBlock::NextKey { kind } => match *kind {
NextKeyKind::ForwardKey {
key_id,
public_key,
reverse_key_requested,
} => {
out.put_u8(GarlicMessageType::NextKey.as_u8());
out.put_u16(if public_key.is_some() { 35u16 } else { 3u16 });
out.put_u8(match (public_key.is_some(), reverse_key_requested) {
(true, true) => 0x01 | 0x04, (true, false) => 0x01, (false, true) => 0x04, (false, false) => panic!(
"state mismatch: no public key and reverse key not requested"
),
});
out.put_u16(key_id);
if let Some(public_key) = public_key {
out.put_slice(public_key.as_ref());
}
}
NextKeyKind::ReverseKey { key_id, public_key } => {
out.put_u8(GarlicMessageType::NextKey.as_u8());
match public_key {
Some(public_key) => {
out.put_u16(35u16);
out.put_u8(0x01 | 0x02); out.put_u16(key_id);
out.put_slice(public_key.as_ref());
}
None => {
out.put_u16(3u16);
out.put_u8(0x02); out.put_u16(key_id);
}
}
}
},
GarlicMessageBlock::AckRequest => {
out.put_u8(GarlicMessageType::AckRequest.as_u8());
out.put_u16(1u16);
out.put_u8(0u8); }
GarlicMessageBlock::Ack { acks } => {
out.put_u8(GarlicMessageType::Ack.as_u8());
out.put_u16(acks.len() as u16 * 4);
for (tag_set_id, tag_index) in acks {
out.put_u16(tag_set_id);
out.put_u16(tag_index);
}
}
block => todo!("unimplemented block: {block:?}"),
}
}
out.freeze().to_vec()
}
}
pub struct GarlicClove {
pub message_type: MessageType,
pub message_id: MessageId,
pub expiration: Duration,
pub delivery_instructions: OwnedDeliveryInstructions,
pub message_body: Vec<u8>,
}
impl fmt::Debug for GarlicClove {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GarlicClove")
.field("message_type", &self.message_type)
.field("message_id", &self.message_id)
.field("expiration", &self.expiration)
.field("delivery_instructions", &self.delivery_instructions)
.field("message_len", &self.message_body.len())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unsupported_garlic_message_block() {
let mut message = GarlicMessageBuilder::default()
.with_date_time(1337u32)
.with_garlic_clove(
MessageType::DatabaseStore,
MessageId::from(1337u32),
Duration::from_secs(1337u64),
DeliveryInstructions::Local,
&vec![1, 2, 3, 4],
)
.build();
message.extend_from_slice(&{
let mut out = BytesMut::with_capacity(1 + 2 + 2);
out.put_u8(15u8); out.put_u16(15u16);
out.put_slice(&vec![0xaa; 15]);
out.to_vec()
});
message.extend_from_slice(&{
let mut out = BytesMut::with_capacity(1 + 2 + 2);
out.put_u8(GarlicMessageType::DateTime.as_u8());
out.put_u16(4u16);
out.put_u32(1338u32);
out.to_vec()
});
let message = GarlicMessage::parse(&message).unwrap();
assert_eq!(message.blocks.len(), 3);
match message.blocks[0] {
GarlicMessageBlock::DateTime { timestamp } => assert_eq!(timestamp, 1337u32),
_ => panic!("invalid garlic block"),
}
match &message.blocks[1] {
GarlicMessageBlock::GarlicClove {
message_type,
message_id,
expiration,
delivery_instructions,
message_body,
} => {
assert_eq!(message_type, &MessageType::DatabaseStore);
assert_eq!(message_id, &MessageId::from(1337u32));
assert_eq!(expiration, &Duration::from_secs(1337u64));
assert!(std::matches!(
delivery_instructions,
DeliveryInstructions::Local
));
assert_eq!(message_body, &vec![1, 2, 3, 4]);
}
_ => panic!("invalid garlic block"),
}
match message.blocks[2] {
GarlicMessageBlock::DateTime { timestamp } => assert_eq!(timestamp, 1338u32),
_ => panic!("invalid garlic block"),
}
}
}