#![cfg_attr(feature = "cargo-clippy", allow(clippy::useless_let_if_seq))]
use std::num::NonZeroUsize;
use nom::bits;
use nom::branch::alt;
use nom::bytes::streaming::take;
use nom::combinator::{map, map_opt};
use nom::number::streaming::{be_u32, be_u8};
use nom::sequence::{preceded, tuple};
use nom::Err;
use num_traits::FromPrimitive;
use crate::de::Deserialize;
use crate::errors::{Error, IResult, Result};
use crate::packet::packet_sum::Packet;
use crate::packet::{
CompressedData, LiteralData, Marker, ModDetectionCode, OnePassSignature, PublicKey,
PublicKeyEncryptedSessionKey, PublicSubkey, SecretKey, SecretSubkey, Signature,
SymEncryptedData, SymEncryptedProtectedData, SymKeyEncryptedSessionKey, Trust, UserAttribute,
UserId,
};
use crate::types::{PacketLength, Tag, Version};
use crate::util::{u16_as_usize, u32_as_usize, u8_as_usize};
fn old_packet_header(i: &[u8]) -> IResult<&[u8], (Version, Tag, PacketLength)> {
#[allow(non_snake_case)]
bits::bits::<_, _, crate::errors::Error, _, _>(|I| {
use bits::streaming::{tag, take};
let (I, (_, ver, tag, len_type)) = tuple((
tag(0b1, 1usize),
map_opt(tag(0b0, 1usize), Version::from_u8),
map_opt(take(4usize), Tag::from_u8),
take(2usize),
))(I)?;
let (I, len) = match len_type {
0 => map(take(8usize), |val| u8_as_usize(val).into())(I)?,
1 => map(take(16usize), |val| u16_as_usize(val).into())(I)?,
2 => map(take(32usize), |val| u32_as_usize(val).into())(I)?,
3 => (I, PacketLength::Indeterminated),
_ => {
return Err(nom::Err::Error(crate::errors::Error::ParsingError(
nom::error::ErrorKind::Switch,
)))
}
};
Ok((I, (ver, tag, len)))
})(i)
}
fn read_packet_len(i: &[u8]) -> IResult<&[u8], PacketLength> {
let (i, olen) = be_u8(i)?;
match olen {
0..=191 => Ok((i, (olen as usize).into())),
192..=223 => map(be_u8, |a| {
(((olen as usize - 192) << 8) + 192 + a as usize).into()
})(i),
224..=254 => Ok((i, PacketLength::Partial(1 << (olen as usize & 0x1F)))),
255 => map(be_u32, |v| u32_as_usize(v).into())(i),
}
}
fn read_partial_bodies(input: &[u8], len: usize) -> IResult<&[u8], ParseResult<'_>> {
if let Some(size) = NonZeroUsize::new(len.saturating_sub(input.len())) {
return Err(Err::Incomplete(nom::Needed::Size(size)));
}
let mut out = vec![&input[0..len]];
let mut rest = &input[len..];
loop {
let res = read_packet_len(rest)?;
match res.1 {
PacketLength::Partial(len) => {
if let Some(size) = NonZeroUsize::new(len.saturating_sub(res.0.len())) {
return Err(Err::Incomplete(nom::Needed::Size(size)));
}
out.push(&res.0[0..len]);
rest = &res.0[len..];
}
PacketLength::Fixed(len) => {
if let Some(size) = NonZeroUsize::new(len.saturating_sub(res.0.len())) {
return Err(Err::Incomplete(nom::Needed::Size(size)));
}
out.push(&res.0[0..len]);
rest = &res.0[len..];
break;
}
PacketLength::Indeterminated => {
out.push(res.0);
rest = &[];
break;
}
}
}
Ok((rest, ParseResult::Partial(out)))
}
fn new_packet_header(i: &[u8]) -> IResult<&[u8], (Version, Tag, PacketLength)> {
use bits::streaming::*;
#[allow(non_snake_case)]
bits::bits(|I| {
preceded(
tag(0b1, 1usize),
tuple((
map_opt(tag(0b1, 1usize), Version::from_u8),
map_opt(take(6usize), Tag::from_u8),
bits::bytes(read_packet_len),
)),
)(I)
})(i)
}
#[derive(Debug)]
pub enum ParseResult<'a> {
Fixed(&'a [u8]),
Indeterminated,
Partial(Vec<&'a [u8]>),
}
pub fn parser(i: &[u8]) -> IResult<&[u8], (Version, Tag, PacketLength, ParseResult<'_>)> {
let (i, head) = alt((new_packet_header, old_packet_header))(i)?;
let (i, body) = match head.2 {
PacketLength::Fixed(length) => map(take(length), ParseResult::Fixed)(i),
PacketLength::Indeterminated => Ok((i, ParseResult::Indeterminated)),
PacketLength::Partial(length) => read_partial_bodies(i, length),
}?;
Ok((i, (head.0, head.1, head.2, body)))
}
pub fn body_parser(ver: Version, tag: Tag, body: &[u8]) -> Result<Packet> {
let res: Result<Packet> = match tag {
Tag::PublicKeyEncryptedSessionKey => {
PublicKeyEncryptedSessionKey::from_slice(ver, body).map(Into::into)
}
Tag::Signature => Signature::from_slice(ver, body).map(Into::into),
Tag::SymKeyEncryptedSessionKey => {
SymKeyEncryptedSessionKey::from_slice(ver, body).map(Into::into)
}
Tag::OnePassSignature => OnePassSignature::from_slice(ver, body).map(Into::into),
Tag::SecretKey => SecretKey::from_slice(ver, body).map(Into::into),
Tag::PublicKey => PublicKey::from_slice(ver, body).map(Into::into),
Tag::SecretSubkey => SecretSubkey::from_slice(ver, body).map(Into::into),
Tag::CompressedData => CompressedData::from_slice(ver, body).map(Into::into),
Tag::SymEncryptedData => SymEncryptedData::from_slice(ver, body).map(Into::into),
Tag::Marker => Marker::from_slice(ver, body).map(Into::into),
Tag::LiteralData => LiteralData::from_slice(ver, body).map(Into::into),
Tag::Trust => Trust::from_slice(ver, body).map(Into::into),
Tag::UserId => UserId::from_slice(ver, body).map(Into::into),
Tag::PublicSubkey => PublicSubkey::from_slice(ver, body).map(Into::into),
Tag::UserAttribute => UserAttribute::from_slice(ver, body).map(Into::into),
Tag::SymEncryptedProtectedData => {
SymEncryptedProtectedData::from_slice(ver, body).map(Into::into)
}
Tag::ModDetectionCode => ModDetectionCode::from_slice(ver, body).map(Into::into),
};
match res {
Ok(res) => Ok(res),
Err(Error::Incomplete(n)) => Err(Error::Incomplete(n)),
Err(err) => {
warn!("invalid packet: {:?} {:?}\n{}", err, tag, hex::encode(body));
Err(Error::InvalidPacketContent(Box::new(err)))
}
}
}