#![cfg_attr(feature = "cargo-clippy", allow(clippy::useless_let_if_seq))]
use nom::{self, be_u32, be_u8, Err, IResult};
use num_traits::FromPrimitive;
use crate::de::Deserialize;
use crate::errors::{Error, 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};
#[rustfmt::skip]
named!(old_packet_header(&[u8]) -> (Version, Tag, PacketLength), bits!(do_parse!(
tag_bits!(u8, 1, 1)
>> ver: map_opt!(tag_bits!(u8, 1, 0), Version::from_u8)
>> tag: map_opt!(take_bits!(u8, 4), Tag::from_u8)
>> len_type: take_bits!(u8, 2)
>> len: switch!(value!(len_type),
0 => map!(take_bits!(u8, 8), |val| u8_as_usize(val).into()) |
1 => map!(take_bits!(u16, 16), |val| u16_as_usize(val).into()) |
2 => map!(take_bits!(u32, 32), |val| u32_as_usize(val).into()) |
3 => value!(PacketLength::Indeterminated)
)
>> ((ver, tag, len))
)));
#[rustfmt::skip]
named!(read_packet_len(&[u8]) -> PacketLength, do_parse!(
olen: be_u8
>> len: switch!(value!(olen),
0..=191 => value!((olen as usize).into()) |
192..=223 => map!(be_u8, |a| {
(((olen as usize - 192) << 8) + 192 + a as usize).into()
}) |
224..=254 => value!(PacketLength::Partial(1 << (olen as usize & 0x1F))) |
255 => map!(be_u32, |v| u32_as_usize(v).into())
)
>> (len)
));
fn read_partial_bodies(input: &[u8], len: usize) -> IResult<&[u8], ParseResult<'_>> {
if input.len() < len {
return Err(Err::Incomplete(nom::Needed::Size(len - input.len())));
}
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 res.0.len() < len {
return Err(Err::Incomplete(nom::Needed::Size(len - res.0.len())));
}
out.push(&res.0[0..len]);
rest = &res.0[len..];
}
PacketLength::Fixed(len) => {
if res.0.len() < len {
return Err(Err::Incomplete(nom::Needed::Size(len - res.0.len())));
}
out.push(&res.0[0..len]);
rest = &res.0[len..];
break;
}
PacketLength::Indeterminated => {
out.push(res.0);
rest = &[];
break;
}
}
}
Ok((rest, ParseResult::Partial(out)))
}
#[rustfmt::skip]
named!(new_packet_header(&[u8]) -> (Version, Tag, PacketLength), bits!(do_parse!(
tag_bits!(u8, 1, 1)
>> ver: map_opt!(tag_bits!(u8, 1, 1), Version::from_u8)
>> tag: map_opt!(take_bits!(u8, 6), Tag::from_u8)
>> len: bytes!(read_packet_len)
>> ((ver, tag, len))
)));
#[derive(Debug)]
pub enum ParseResult<'a> {
Fixed(&'a [u8]),
Indeterminated,
Partial(Vec<&'a [u8]>),
}
#[rustfmt::skip]
named!(pub parser<(Version, Tag, PacketLength, ParseResult<'_>)>, do_parse!(
head: alt!(new_packet_header | old_packet_header)
>> body: switch!(value!(&head.2),
PacketLength::Fixed(length) => map!(take!(*length), ParseResult::Fixed) |
PacketLength::Indeterminated => value!(ParseResult::Indeterminated) |
PacketLength::Partial(length) => call!(read_partial_bodies, *length)
)
>> (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)))
}
}
}