#![allow(clippy::useless_let_if_seq)]
use log::warn;
use nom::bits;
use nom::branch::alt;
use nom::combinator::{map, map_res};
use nom::number::streaming::{be_u32, be_u8};
use nom::sequence::{preceded, tuple};
use crate::errors::{Error, IResult, Result};
use crate::packet::packet_sum::Packet;
use crate::packet::{
CompressedData, LiteralData, Marker, ModDetectionCode, OnePassSignature, Padding, 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_res(tag(0b0, 1usize), Version::try_from),
map_res(take(4usize), u8::try_into),
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::Indeterminate),
_ => {
return Err(nom::Err::Error(crate::errors::Error::ParsingError(
nom::error::ErrorKind::Switch,
)))
}
};
Ok((I, (ver, tag, len)))
})(i)
}
pub(crate) 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 => {
let (i, len) = be_u32(i)?;
Ok((i, (len as usize).into()))
}
}
}
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_res(tag(0b1, 1usize), Version::try_from),
map(take(6usize), u8::into),
bits::bytes(read_packet_len),
)),
)(I)
})(i)
}
pub fn parser(i: &[u8]) -> IResult<&[u8], (Version, Tag, PacketLength)> {
let (i, head) = alt((new_packet_header, old_packet_header))(i)?;
Ok((i, head))
}
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),
Tag::Padding => Padding::from_slice(ver, body).map(Into::into),
Tag::Other(20) => {
unimplemented_err!("GnuPG-proprietary 'OCB Encrypted Data Packet' is unsupported")
}
Tag::Other(22..=39) => {
return Err(Error::InvalidPacketContent(Box::new(Error::Message(
format!("Unassigned Critical Packet type {:?}", tag),
))));
}
Tag::Other(40..=59) => {
unsupported_err!("Unsupported but non-critical packet type: {:?}", tag)
}
Tag::Other(other) => unimplemented_err!("Unknown packet type: {}", other),
};
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)))
}
}
}