use crate::{
Error,
Result,
};
use crate::packet::tag::Tag;
mod ctb;
pub use self::ctb::{
CTB,
CTBOld,
CTBNew,
PacketLengthType,
};
#[derive(Clone, Debug)]
pub struct Header {
ctb: CTB,
length: BodyLength,
}
assert_send_and_sync!(Header);
impl Header {
pub fn new(ctb: CTB, length: BodyLength) -> Self {
Header { ctb, length }
}
pub fn ctb(&self) -> &CTB {
&self.ctb
}
pub fn length(&self) -> &BodyLength {
&self.length
}
pub fn valid(&self, future_compatible: bool) -> Result<()> {
let tag = self.ctb.tag();
match tag {
Tag::Reserved =>
return Err(Error::UnsupportedPacketType(tag).into()),
Tag::Unknown(_) | Tag::Private(_) if !future_compatible =>
return Err(Error::UnsupportedPacketType(tag).into()),
_ => (),
}
if tag == Tag::Literal || tag == Tag::CompressedData
|| tag == Tag::SED || tag == Tag::SEIP
|| tag == Tag::AED
{
match self.length {
BodyLength::Indeterminate => (),
BodyLength::Partial(l) => {
if l < 512 {
return Err(Error::MalformedPacket(
format!("Partial body length must be \
at least 512 (got: {})",
l)).into());
}
}
BodyLength::Full(l) => {
if tag == Tag::SED && (l < (8 + 2 + 6)) { return Err(Error::MalformedPacket(
format!("{} packet's length must be \
at least 16 bytes in length (got: {})",
tag, l)).into());
} else if tag == Tag::SEIP
&& (l < (1 + 8 + 2 + 6 + 20)) {
return Err(Error::MalformedPacket(
format!("{} packet's length minus 1 must be \
at least 37 bytes in length (got: {})",
tag, l)).into());
} else if tag == Tag::CompressedData && l == 0 {
return Err(Error::MalformedPacket(
format!("{} packet's length must be \
at least 1 byte (got ({})",
tag, l)).into());
} else if tag == Tag::Literal && l < 6 {
return Err(Error::MalformedPacket(
format!("{} packet's length must be \
at least 6 bytes (got: ({})",
tag, l)).into());
}
}
}
} else {
match self.length {
BodyLength::Indeterminate =>
return Err(Error::MalformedPacket(
format!("Indeterminite length encoding \
not allowed for {} packets",
tag)).into()),
BodyLength::Partial(_) =>
return Err(Error::MalformedPacket(
format!("Partial Body Chunking not allowed \
for {} packets",
tag)).into()),
BodyLength::Full(l) => {
let valid = match tag {
Tag::Signature =>
(10..(10 + 2 * 64 * 1024 + 64 * 1024 )).contains(&l),
Tag::SKESK =>
(3..10 * 1024).contains(&l),
Tag::PKESK =>
(10 < l && l < 10 * 1024)
|| (5 <= l && l < 10 * 1024),
Tag::OnePassSig if ! future_compatible =>
l == 13 || (6 + 32..6 + 32 + 256).contains(&l), Tag::OnePassSig => l < 1024,
Tag::PublicKey | Tag::PublicSubkey
| Tag::SecretKey | Tag::SecretSubkey =>
6 < l && l < 1024 * 1024,
Tag::Trust => true,
Tag::UserID =>
l < 32 * 1024,
Tag::UserAttribute =>
2 <= l,
Tag::MDC => l == 20,
Tag::Literal | Tag::CompressedData
| Tag::SED | Tag::SEIP | Tag::AED =>
unreachable!("handled in the data-packet branch"),
Tag::Unknown(_) | Tag::Private(_) => true,
Tag::Marker => l == 3,
Tag::Reserved => true,
Tag::Padding => true,
};
if ! valid {
return Err(Error::MalformedPacket(
format!("Invalid size ({} bytes) for a {} packet",
l, tag)).into())
}
}
}
}
Ok(())
}
}
#[derive(Debug)]
#[derive(PartialEq)]
#[derive(Clone, Copy)]
pub enum BodyLength {
Full(u32),
Partial(u32),
Indeterminate,
}
assert_send_and_sync!(BodyLength);
#[cfg(test)]
mod tests {
use super::*;
use crate::packet::Packet;
use crate::parse::{
Cookie, Dearmor, PacketParserBuilder, PacketParserResult, Parse,
};
use crate::serialize::SerializeInto;
quickcheck! {
fn parser_alignment(p: Packet) -> bool {
let verbose = false;
let buf = p.to_vec().expect("Failed to serialize packet");
let mut reader = buffered_reader::Memory::with_cookie(
&buf, Cookie::default());
let header = Header::parse(&mut reader).unwrap();
if verbose {
eprintln!("header parsed: {:?}", header);
}
header.valid(true).unwrap();
header.valid(false).unwrap();
let ppr =
PacketParserBuilder::from_bytes(&buf).unwrap()
.dearmor(Dearmor::Disabled)
.buffer_unread_content()
.build().unwrap();
let (p, ppr) = match ppr {
PacketParserResult::Some(pp) => {
pp.next().unwrap()
},
PacketParserResult::EOF(eof) =>
panic!("no packet found: {:?}", eof),
};
if verbose {
eprintln!("packet parser parsed: {:?}", p);
}
if let PacketParserResult::Some(pp) = ppr {
panic!("Excess data after packet: {:?}", pp)
}
true
}
}
}