use std;
use std::io;
use std::io::prelude::*;
use std::cmp;
use std::collections::HashMap;
use std::str;
use std::mem;
use std::fmt;
use std::path::Path;
use time;
use failure;
use nettle;
use ::buffered_reader::*;
use {
crypto::{aead, Hash},
Result,
CTB,
BodyLength,
crypto::s2k::S2K,
Error,
packet::Tag,
Header,
packet::Unknown,
packet::signature::Signature,
packet::OnePassSig,
packet::Key,
packet::UserID,
packet::UserAttribute,
packet::Literal,
packet::CompressedData,
packet::{SKESK, SKESK4, SKESK5},
packet::SEIP,
packet::MDC,
packet::AED,
Packet,
KeyID,
packet::key::SecretKey,
crypto::SessionKey,
packet::PKESK,
};
use constants::{
AEADAlgorithm,
CompressionAlgorithm,
Curve,
SignatureType,
HashAlgorithm,
PublicKeyAlgorithm,
SymmetricAlgorithm,
};
use conversions::Time;
use crypto::{self, mpis::{PublicKey, MPI}};
use crypto::symmetric::{Decryptor, BufferedReaderDecryptor};
use message;
use message::MessageValidator;
mod partial_body;
use self::partial_body::BufferedReaderPartialBodyFilter;
use packet::signature::subpacket::SubpacketArea;
mod key;
mod packet_pile_parser;
pub use self::packet_pile_parser::PacketPileParser;
mod hashed_reader;
pub(crate) use self::hashed_reader::HashedReader;
mod packet_parser_builder;
pub use self::packet_parser_builder::PacketParserBuilder;
pub mod map;
mod mpis;
pub mod stream;
const TRACE : bool = false;
#[cfg(test)]
macro_rules! bytes {
( $x:expr ) => { include_bytes!(concat!("../../tests/data/messages/", $x)) };
}
#[cfg(test)]
use std::path::PathBuf;
#[cfg(test)]
fn path_to(artifact: &str) -> PathBuf {
[env!("CARGO_MANIFEST_DIR"), "tests", "data", "messages", artifact]
.iter().collect()
}
macro_rules! destructures_to {
( $error: pat = $expr:expr ) => {
{
let x = $expr;
if let $error = x {
true
} else {
false
}
}
};
}
pub trait Parse<'a, T> {
fn from_reader<R: 'a + Read>(reader: R) -> Result<T>;
fn from_file<P: AsRef<Path>>(path: P) -> Result<T>
{
Self::from_reader(::std::fs::File::open(path)?)
}
fn from_bytes(data: &'a [u8]) -> Result<T>
{
Self::from_reader(io::Cursor::new(data))
}
}
const MAX_RECURSION_DEPTH : u8 = 16;
pub(crate) struct PacketHeaderParser<'a> {
reader: buffered_reader::Dup<'a, Cookie>,
header: Header,
header_bytes: Vec<u8>,
path: Vec<usize>,
state: PacketParserState,
map: Option<map::Map>,
}
macro_rules! make_php_try {
($parser:expr) => {
macro_rules! php_try {
($e:expr) => {
match $e {
Ok(b) => {
Ok(b)
},
Err(e) => {
let e = match e.downcast::<io::Error>() {
Ok(e) =>
if let io::ErrorKind::UnexpectedEof = e.kind() {
return $parser.error(e.into());
} else {
e.into()
},
Err(e) => e,
};
let e = match e.downcast::<Error>() {
Ok(e) => match e {
Error::MalformedMPI(_) =>
return $parser.error(e.into()),
_ =>
e.into(),
},
Err(e) => e,
};
Err(e)
},
}?
};
}
};
}
impl<'a> std::fmt::Debug for PacketHeaderParser<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("PacketHeaderParser")
.field("header", &self.header)
.field("path", &self.path)
.field("reader", &self.reader)
.field("state", &self.state)
.field("map", &self.map)
.finish()
}
}
impl<'a> PacketHeaderParser<'a> {
fn new(inner: Box<'a + BufferedReader<Cookie>>,
state: PacketParserState,
path: Vec<usize>, header: Header,
header_bytes: Vec<u8>) -> Self
{
assert!(path.len() > 0);
let mut cookie = Cookie::default();
cookie.level = inner.cookie_ref().level;
let map = if state.settings.map {
Some(map::Map::new(header_bytes.clone()))
} else {
None
};
PacketHeaderParser {
reader: buffered_reader::Dup::with_cookie(inner, cookie),
header: header,
header_bytes: header_bytes.clone(),
path: path,
state: state,
map: map,
}
}
fn new_naked(inner: Box<'a + BufferedReader<Cookie>>) -> Self {
PacketHeaderParser::new(inner,
PacketParserState::new(Default::default()),
vec![ 0 ],
Header {
ctb: CTB::new(Tag::Reserved),
length: BodyLength::Full(0),
},
Vec::new())
}
fn ok(mut self, packet: Packet) -> Result<PacketParser<'a>> {
let total_out = self.reader.total_out();
let mut reader = if self.state.settings.map {
let body = self.reader.steal_eof()?;
if body.len() > 0 {
self.field("body", body.len());
}
let mut inner = Box::new(self.reader).into_inner().unwrap();
let mut data = Vec::with_capacity(total_out + body.len());
data.extend_from_slice(&inner.buffer()[..total_out]);
data.extend(body);
self.map.as_mut().unwrap().finalize(data);
inner
} else {
Box::new(self.reader).into_inner().unwrap()
};
reader.data_consume_hard(total_out).unwrap();
Ok(PacketParser {
header: self.header,
packet: packet,
path: self.path,
last_path: vec![],
reader: reader,
content_was_read: false,
decrypted: true,
finished: false,
map: self.map,
state: self.state,
})
}
fn fail(self, reason: &'static str) -> Result<PacketParser<'a>> {
self.error(Error::MalformedPacket(reason.into()).into())
}
fn error(self, error: failure::Error) -> Result<PacketParser<'a>> {
Unknown::parse(self, error)
}
fn field(&mut self, name: &'static str, size: usize) {
if let Some(ref mut map) = self.map {
map.add(name, size)
}
}
fn parse_u8(&mut self, name: &'static str) -> Result<u8> {
self.field(name, 1);
Ok(self.reader.data_consume_hard(1)?[0])
}
fn parse_be_u16(&mut self, name: &'static str) -> Result<u16> {
self.field(name, 2);
Ok(self.reader.read_be_u16()?)
}
fn parse_be_u32(&mut self, name: &'static str) -> Result<u32> {
self.field(name, 4);
Ok(self.reader.read_be_u32()?)
}
fn parse_bytes(&mut self, name: &'static str, amount: usize)
-> Result<Vec<u8>> {
self.field(name, amount);
Ok(self.reader.steal(amount)?)
}
fn parse_bytes_eof(&mut self, name: &'static str) -> Result<Vec<u8>> {
let r = self.reader.steal_eof()?;
self.field(name, r.len());
Ok(r)
}
fn recursion_depth(&self) -> isize {
self.path.len() as isize - 1
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub(crate) enum HashesFor {
Nothing,
MDC,
Signature,
}
#[derive(Copy, Clone, PartialEq, Debug)]
enum Hashing {
Enabled,
Notarized,
Disabled,
}
#[derive(Debug)]
pub(crate) struct Cookie {
level: Option<isize>,
hashes_for: HashesFor,
hashing: Hashing,
saw_last: bool,
sig_groups: Vec<SignatureGroup>,
sig_groups_max_len: usize,
hash_stash: Option<Vec<u8>>,
fake_eof: bool,
}
pub(crate) struct SignatureGroup {
ops_count: usize,
pub(crate) hashes: HashMap<HashAlgorithm, Box<nettle::Hash>>,
}
impl fmt::Debug for SignatureGroup {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let algos = self.hashes.keys()
.collect::<Vec<&HashAlgorithm>>();
f.debug_struct("Cookie")
.field("ops_count", &self.ops_count)
.field("hashes", &algos)
.finish()
}
}
impl Default for SignatureGroup {
fn default() -> Self {
SignatureGroup {
ops_count: 0,
hashes: HashMap::new(),
}
}
}
impl SignatureGroup {
fn clear(&mut self) {
self.ops_count = 0;
self.hashes.clear();
}
}
impl Default for Cookie {
fn default() -> Self {
Cookie {
level: None,
hashing: Hashing::Enabled,
hashes_for: HashesFor::Nothing,
saw_last: false,
sig_groups: vec![Default::default()],
sig_groups_max_len: 1,
hash_stash: None,
fake_eof: false,
}
}
}
impl Cookie {
fn new(level: isize) -> Cookie {
Cookie {
level: Some(level),
hashing: Hashing::Enabled,
hashes_for: HashesFor::Nothing,
saw_last: false,
sig_groups: vec![Default::default()],
sig_groups_max_len: 1,
hash_stash: None,
fake_eof: false,
}
}
pub(crate) fn sig_group(&self) -> &SignatureGroup {
assert!(self.sig_groups.len() > 0);
&self.sig_groups[self.sig_groups.len() - 1]
}
pub(crate) fn sig_group_mut(&mut self) -> &mut SignatureGroup {
assert!(self.sig_groups.len() > 0);
let len = self.sig_groups.len();
&mut self.sig_groups[len - 1]
}
fn signature_level(&self) -> usize {
self.sig_groups_max_len - self.sig_groups.len()
}
fn sig_group_unused(&self) -> bool {
assert!(self.sig_groups.len() > 0);
self.sig_groups[self.sig_groups.len() - 1].ops_count == 0
}
fn sig_group_push(&mut self) {
self.sig_groups.push(Default::default());
self.sig_groups_max_len += 1;
}
fn sig_group_pop(&mut self) {
if self.sig_groups.len() == 1 {
self.sig_groups[0].clear();
self.hashes_for = HashesFor::Nothing;
} else {
self.sig_groups.pop();
}
}
}
impl Cookie {
fn hashing(reader: &mut BufferedReader<Cookie>,
how: Hashing, level: isize) {
let mut reader : Option<&mut BufferedReader<Cookie>>
= Some(reader);
while let Some(r) = reader {
{
let cookie = r.cookie_mut();
if let Some(br_level) = cookie.level {
if br_level < level {
break;
}
if br_level == level
&& cookie.hashes_for == HashesFor::Signature {
cookie.hashing = how;
}
} else {
break;
}
}
reader = r.get_mut();
}
}
#[allow(dead_code)]
fn dump(reader: &BufferedReader<Cookie>) {
let mut i = 1;
let mut reader : Option<&BufferedReader<Cookie>> = Some(reader);
while let Some(r) = reader {
{
let cookie = r.cookie_ref();
eprint!(" {}. {}, level: {:?}",
i, r, cookie.level);
if cookie.hashes_for != HashesFor::Nothing {
eprint!(", hashes for: {:?}", cookie.hashes_for);
}
eprint!("\n");
}
reader = r.get_ref();
i = i + 1;
}
}
}
fn buffered_reader_stack_pop<'a>(
mut reader: Box<BufferedReader<Cookie> + 'a>, depth: isize)
-> Result<(bool, Box<BufferedReader<Cookie> + 'a>)>
{
tracer!(TRACE, "buffered_reader_stack_pop", depth);
t!("(reader level: {:?}, pop through: {})",
reader.cookie_ref().level, depth);
let mut last_level = None;
while let Some(level) = reader.cookie_ref().level {
assert!(level <= depth);
if level >= depth {
let fake_eof = reader.cookie_ref().fake_eof;
t!("top reader at level {:?} (fake eof: {}), pop through: {})",
reader.cookie_ref().level, fake_eof, depth);
let (dropping_content, dropped_content)
= if Some(level) != last_level {
(true, reader.drop_eof()?)
} else {
assert_eq!(reader.buffer().len(), 0);
(false, false)
};
t!("popping level {:?} reader, {}dropping content ({}), \
reader: {:?}",
reader.cookie_ref().level,
if dropping_content { "" } else { "not " },
if dropped_content { "something dropped" }
else { "nothing to drop" },
reader);
reader = reader.into_inner().unwrap();
if level == depth && fake_eof {
t!("Popped a fake EOF reader at level {}, stopping.", depth);
return Ok((true, reader));
}
} else {
break;
}
last_level = Some(level);
}
Ok((false, reader))
}
#[derive(Clone, Debug)]
struct PacketParserSettings {
max_recursion_depth: u8,
buffer_unread_content: bool,
map: bool,
}
impl Default for PacketParserSettings {
fn default() -> Self {
PacketParserSettings {
max_recursion_depth: MAX_RECURSION_DEPTH,
buffer_unread_content: false,
map: false,
}
}
}
impl S2K {
fn parse<'a>(php: &mut PacketHeaderParser<'a>) -> Result<Self>
{
let s2k = php.parse_u8("s2k_type")?;
let ret = match s2k {
0 => S2K::Simple {
hash: HashAlgorithm::from(php.parse_u8("s2k_hash_algo")?),
},
1 => S2K::Salted {
hash: HashAlgorithm::from(php.parse_u8("s2k_hash_algo")?),
salt: Self::read_salt(php)?,
},
3 => S2K::Iterated {
hash: HashAlgorithm::from(php.parse_u8("s2k_hash_algo")?),
salt: Self::read_salt(php)?,
iterations: S2K::decode_count(php.parse_u8("s2k_count")?),
},
100...110 => S2K::Private(s2k),
u => S2K::Unknown(u),
};
Ok(ret)
}
fn read_salt<'a>(php: &mut PacketHeaderParser<'a>) -> Result<[u8; 8]> {
let mut b = [0u8; 8];
b.copy_from_slice(&php.parse_bytes("s2k_salt", 8)?);
Ok(b)
}
}
impl<'a> Parse<'a, S2K> for S2K {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let bio = buffered_reader::Generic::with_cookie(
reader, None, Cookie::default());
let mut parser = PacketHeaderParser::new_naked(Box::new(bio));
Self::parse(&mut parser)
}
}
impl Header {
pub(crate) fn parse<R: BufferedReader<C>, C> (bio: &mut R)
-> Result<Header>
{
let ctb = CTB::from_ptag(bio.data_consume_hard(1)?[0])?;
let length = match ctb {
CTB::New(_) => BodyLength::parse_new_format(bio)?,
CTB::Old(ref ctb) =>
BodyLength::parse_old_format(bio, ctb.length_type)?,
};
return Ok(Header { ctb: ctb, length: length });
}
pub fn plausible<R: BufferedReader<C>, C>(reader: &mut R)
-> Result<()>
{
use packet::Tag;
let mut data = reader.data(6)?;
if data.len() > 6 {
data = &data[..6];
}
if data.len() == 0 {
return Ok(());
}
let header = Self::from_reader(data)?;
match header.ctb.tag {
Tag::PKESK | Tag::SKESK |
Tag::PublicKey | Tag::PublicSubkey |
Tag::SecretKey | Tag::SecretSubkey |
Tag::UserID |
Tag::Signature => {
let length = match header.length {
BodyLength::Full(l) => l ,
BodyLength::Partial(l) => l ,
BodyLength::Indeterminate =>
return Err(Error::MalformedMessage(
format!("Indeterminate body length invalid for {}",
header.ctb.tag).into()).into()),
};
if length > 8 * 1024 * 1024 {
return Err(Error::MalformedMessage(
format!("{} too long ({} bytes)",
header.ctb.tag, length).into()).into());
}
},
Tag::Reserved | Tag::Marker | Tag::Trust
| Tag::MDC | Tag::Unknown(_) =>
return Err(Error::MalformedMessage(
format!("OpenPGP messages don't start with {}",
header.ctb.tag).into()).into()),
Tag::OnePassSig | Tag::CompressedData
| Tag::SED | Tag::Literal | Tag::UserAttribute
| Tag::SEIP | Tag::AED | Tag::Private(_) => (),
}
Ok(())
}
}
impl<'a> Parse<'a, Header> for Header {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self>
{
let mut reader = buffered_reader::Generic::with_cookie(
reader, None, Cookie::default());
Header::parse(&mut reader)
}
}
impl Unknown {
fn parse<'a>(php: PacketHeaderParser<'a>, error: failure::Error)
-> Result<PacketParser<'a>>
{
let tag = php.header.ctb.tag;
php.ok(Packet::Unknown(Unknown::new(tag, error)))
.map(|pp| pp.set_decrypted(false))
}
}
#[cfg(test)]
pub(crate) fn to_unknown_packet<R: Read>(reader: R) -> Result<Unknown>
{
let mut reader = buffered_reader::Generic::with_cookie(
reader, None, Cookie::default());
let header = Header::parse(&mut reader)?;
let reader : Box<BufferedReader<Cookie>>
= match header.length {
BodyLength::Full(len) =>
Box::new(buffered_reader::Limitor::with_cookie(
Box::new(reader), len as u64, Cookie::default())),
BodyLength::Partial(len) =>
Box::new(BufferedReaderPartialBodyFilter::with_cookie(
reader, len, true, Cookie::default())),
_ => Box::new(reader),
};
let parser = PacketHeaderParser::new(
reader, PacketParserState::new(Default::default()), vec![ 0 ], header, Vec::new());
let mut pp =
Unknown::parse(parser,
failure::err_msg("explicit conversion to unknown"))?;
pp.buffer_unread_content()?;
pp.finish()?;
if let Packet::Unknown(packet) = pp.packet {
Ok(packet)
} else {
panic!("Internal inconsistency.");
}
}
impl Signature {
pub(crate) fn parse_naked(value: &[u8]) -> Result<Packet> {
let bio = buffered_reader::Memory::with_cookie(
value, Cookie::default());
let parser = PacketHeaderParser::new_naked(Box::new(bio));
let mut pp = Signature::parse(parser)?;
pp.buffer_unread_content()?;
pp.finish()?;
match pp.packet {
Packet::Signature(_) => Ok(pp.packet),
Packet::Unknown(_) => Ok(pp.packet),
_ => panic!("Internal inconsistency."),
}
}
fn parse<'a>(mut php: PacketHeaderParser<'a>)
-> Result<PacketParser<'a>>
{
let indent = php.recursion_depth();
tracer!(TRACE, "Signature::parse", indent);
make_php_try!(php);
let version = php_try!(php.parse_u8("version"));
if version != 4 {
t!("Ignoring version {} packet.", version);
return php.fail("unknown version");
}
let sigtype = php_try!(php.parse_u8("sigtype"));
let pk_algo: PublicKeyAlgorithm = php_try!(php.parse_u8("pk_algo")).into();
let hash_algo = php_try!(php.parse_u8("hash_algo"));
let hashed_area_len = php_try!(php.parse_be_u16("hashed_area_len"));
let hashed_area
= php_try!(php.parse_bytes("hashed_area",
hashed_area_len as usize));
let unhashed_area_len = php_try!(php.parse_be_u16("unhashed_area_len"));
let unhashed_area
= php_try!(php.parse_bytes("unhashed_area",
unhashed_area_len as usize));
let hash_prefix1 = php_try!(php.parse_u8("hash_prefix1"));
let hash_prefix2 = php_try!(php.parse_u8("hash_prefix2"));
if ! pk_algo.can_sign() {
return php.fail("not a signature algorithm");
}
let mpis = php_try!(
crypto::mpis::Signature::parse(pk_algo, &mut php));
let hash_algo = hash_algo.into();
let mut pp = php.ok(Packet::Signature(Signature::new(
sigtype.into(), pk_algo.into(), hash_algo,
SubpacketArea::new(hashed_area),
SubpacketArea::new(unhashed_area),
[hash_prefix1, hash_prefix2],
mpis)))?;
let mut computed_hash = None;
{
let recursion_depth = pp.recursion_depth();
let mut r = (&mut pp.reader).get_mut();
while let Some(tmp) = r {
{
let cookie = tmp.cookie_mut();
assert!(cookie.level.unwrap_or(-1)
<= recursion_depth);
if cookie.level.is_none()
|| cookie.level.unwrap() < recursion_depth - 1 {
break
}
if cookie.hashes_for == HashesFor::Signature {
cookie.sig_group_mut().ops_count -= 1;
if let Some(hash) =
cookie.sig_group().hashes.get(&hash_algo)
{
t!("popped a {:?} HashedReader", hash_algo);
computed_hash = Some((cookie.signature_level(),
hash_algo, hash.clone()));
}
if cookie.sig_group_unused() {
cookie.sig_group_pop();
}
break;
}
}
r = tmp.get_mut();
}
}
if let Some((level, algo, mut hash)) = computed_hash {
if let Packet::Signature(ref mut sig) = pp.packet {
sig.hash(&mut hash);
let mut digest = vec![0u8; hash.digest_size()];
hash.digest(&mut digest);
sig.set_computed_hash(Some((algo, digest)));
sig.set_level(level);
} else {
unreachable!()
}
}
Ok(pp)
}
fn plausible(bio: &mut buffered_reader::Dup<Cookie>, header: &Header) -> Result<()> {
if let BodyLength::Full(len) = header.length {
if len < 11 {
return Err(
Error::MalformedPacket("Packet too short".into()).into());
}
} else {
return Err(
Error::MalformedPacket(
format!("Unexpected body length encoding: {:?}",
header.length)
.into()).into());
}
let data = bio.data(11)?;
if data.len() < 11 {
return Err(
Error::MalformedPacket("Short read".into()).into());
}
let version = data[0];
let sigtype : SignatureType = data[1].into();
let pk_algo : PublicKeyAlgorithm = data[2].into();
let hash_algo : HashAlgorithm = data[3].into();
if version == 4
&& !destructures_to!(SignatureType::Unknown(_) = sigtype)
&& !destructures_to!(PublicKeyAlgorithm::Unknown(_) = pk_algo)
&& !destructures_to!(HashAlgorithm::Unknown(_) = hash_algo)
{
Ok(())
} else {
Err(Error::MalformedPacket("Invalid or unsupported data".into())
.into())
}
}
}
impl<'a> Parse<'a, Signature> for Signature {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParserBuilder::from_reader(reader)?
.buffer_unread_content().finalize()?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::Signature(o), PacketParserResult::EOF(_)) =>
Ok(o),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a Signature packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
#[test]
fn signature_parser_test () {
let data = bytes!("sig.gpg");
{
let pp = PacketParser::from_bytes(data).unwrap().unwrap();
assert_eq!(pp.header.length, BodyLength::Full(307));
if let Packet::Signature(ref p) = pp.packet {
assert_eq!(p.version(), 4);
assert_eq!(p.sigtype(), SignatureType::Binary);
assert_eq!(p.pk_algo(), PublicKeyAlgorithm::RSAEncryptSign);
assert_eq!(p.hash_algo(), HashAlgorithm::SHA512);
assert_eq!(p.hashed_area().data.len(), 29);
assert_eq!(p.unhashed_area().data.len(), 10);
assert_eq!(p.hash_prefix(), &[0x65u8, 0x74]);
assert_eq!(p.mpis().serialized_len(), 258);
} else {
panic!("Wrong packet!");
}
}
}
impl OnePassSig {
fn parse<'a>(mut php: PacketHeaderParser<'a>)
-> Result<PacketParser<'a>>
{
let indent = php.recursion_depth();
tracer!(TRACE, "OnePassSig", indent);
make_php_try!(php);
let version = php_try!(php.parse_u8("version"));
if version != 3 {
t!("Ignoring version {} packet", version);
return php.fail("unknown version");
}
let sigtype = php_try!(php.parse_u8("sigtype"));
let hash_algo = php_try!(php.parse_u8("hash_algo"));
let pk_algo = php_try!(php.parse_u8("pk_algo"));
let mut issuer = [0u8; 8];
issuer.copy_from_slice(&php_try!(php.parse_bytes("issuer", 8)));
let last = php_try!(php.parse_u8("last"));
let hash_algo = hash_algo.into();
let mut sig = OnePassSig::new(sigtype.into());
sig.set_hash_algo(hash_algo);
sig.set_pk_algo(pk_algo.into());
sig.set_issuer(KeyID::from_bytes(&issuer));
sig.set_last_raw(last);
let recursion_depth = php.recursion_depth();
let done = {
let mut done = false;
let mut reader : Option<&mut BufferedReader<Cookie>>
= Some(&mut php.reader);
while let Some(r) = reader {
{
let cookie = r.cookie_mut();
if let Some(br_level) = cookie.level {
if br_level < recursion_depth - 1 {
break;
}
if br_level == recursion_depth - 1
&& cookie.hashes_for == HashesFor::Signature {
if cookie.saw_last {
cookie.sig_group_push();
cookie.saw_last = false;
cookie.hash_stash =
Some(php.header_bytes.clone());
}
if ! cookie.sig_group()
.hashes.contains_key(&hash_algo)
{
if let Ok(ctx) = hash_algo.context() {
cookie.sig_group_mut()
.hashes.insert(hash_algo, ctx);
}
}
cookie.sig_group_mut().ops_count += 1;
cookie.saw_last = last > 0;
done = true;
break;
}
} else {
break;
}
}
reader = r.get_mut();
}
done
};
let mut pp = php.ok(Packet::OnePassSig(sig))?;
if done {
return Ok(pp);
}
let mut algos = Vec::new();
let hash_algo = HashAlgorithm::from(hash_algo);
if hash_algo.is_supported() {
algos.push(hash_algo);
}
assert!(pp.reader.cookie_ref().level <= Some(recursion_depth));
let (fake_eof, reader)
= buffered_reader_stack_pop(Box::new(pp.take_reader()),
recursion_depth)?;
assert!(! fake_eof);
let mut reader = HashedReader::new(
reader, HashesFor::Signature, algos);
reader.cookie_mut().level = Some(recursion_depth - 1);
reader.cookie_mut().sig_group_mut().ops_count += 1;
reader.cookie_mut().saw_last = last > 0;
t!("Pushed a hashed reader, level {:?}", reader.cookie_mut().level);
let mut reader = buffered_reader::Limitor::with_cookie(
Box::new(reader), 0, Cookie::default());
reader.cookie_mut().level = Some(recursion_depth);
pp.reader = Box::new(reader);
Ok(pp)
}
}
#[test]
fn one_pass_sig_parser_test () {
use SignatureType;
use PublicKeyAlgorithm;
let data = bytes!("signed-1.gpg");
let mut pp = PacketParser::from_bytes(data).unwrap().unwrap();
let p = pp.finish().unwrap();
if let &Packet::OnePassSig(ref p) = p {
assert_eq!(p.version(), 3);
assert_eq!(p.sigtype(), SignatureType::Binary);
assert_eq!(p.hash_algo(), HashAlgorithm::SHA512);
assert_eq!(p.pk_algo(), PublicKeyAlgorithm::RSAEncryptSign);
assert_eq!(p.issuer().to_hex(), "7223B56678E02528");
assert_eq!(p.last_raw(), 1);
} else {
panic!("Wrong packet!");
}
}
impl<'a> Parse<'a, OnePassSig> for OnePassSig {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParserBuilder::from_reader(reader)?
.buffer_unread_content().finalize()?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::OnePassSig(o), PacketParserResult::EOF(_)) =>
Ok(o),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a OnePassSig packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
#[test]
fn one_pass_sig_test () {
struct Test<'a> {
filename: &'a str,
hash_prefix: Vec<[u8; 2]>,
};
let tests = [
Test {
filename: "signed-1.gpg",
hash_prefix: vec![ [ 0x83, 0xF5 ] ],
},
Test {
filename: "signed-2-partial-body.gpg",
hash_prefix: vec![ [ 0x2F, 0xBE ] ],
},
Test {
filename: "signed-3-partial-body-multiple-sigs.gpg",
hash_prefix: vec![ [ 0x29, 0x64 ], [ 0xff, 0x7d ] ],
},
];
for test in tests.iter() {
eprintln!("Trying {}...", test.filename);
let mut ppr = PacketParserBuilder::from_file(path_to(test.filename))
.expect(&format!("Reading {}", test.filename)[..])
.finalize().unwrap();
let mut one_pass_sigs = 0;
let mut sigs = 0;
while let PacketParserResult::Some(pp) = ppr {
if let Packet::OnePassSig(_) = pp.packet {
one_pass_sigs += 1;
} else if let Packet::Signature(ref sig) = pp.packet {
eprintln!(" {}:\n prefix: expected: {}, in sig: {}",
test.filename,
::conversions::to_hex(&test.hash_prefix[sigs][..], false),
::conversions::to_hex(sig.hash_prefix(), false));
eprintln!(" computed hash: {}",
::conversions::to_hex(&sig.computed_hash().unwrap().1,
false));
assert_eq!(&test.hash_prefix[sigs], sig.hash_prefix());
assert_eq!(&test.hash_prefix[sigs][..],
&sig.computed_hash().unwrap().1[..2]);
sigs += 1;
} else if one_pass_sigs > 0 {
assert_eq!(one_pass_sigs, test.hash_prefix.len(),
"Number of OnePassSig packets does not match \
number of expected OnePassSig packets.");
}
ppr = pp.recurse().expect("Parsing message").1;
}
assert_eq!(one_pass_sigs, sigs,
"Number of OnePassSig packets does not match \
number of signature packets.");
eprintln!("done.");
}
}
impl Key {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
use std::io::Cursor;
use serialize::Serialize;
make_php_try!(php);
let tag = php.header.ctb.tag;
assert!(tag == Tag::PublicKey
|| tag == Tag::PublicSubkey
|| tag == Tag::SecretKey
|| tag == Tag::SecretSubkey);
let version = php_try!(php.parse_u8("version"));
if version != 4 {
return php.fail("unknown version");
}
let creation_time = php_try!(php.parse_be_u32("creation_time"));
let pk_algo: PublicKeyAlgorithm = php_try!(php.parse_u8("pk_algo")).into();
let mpis = php_try!(PublicKey::parse(pk_algo, &mut php));
let secret = if tag == Tag::SecretKey || tag == Tag::SecretSubkey {
let s2k_usage = php_try!(php.parse_u8("s2k_usage"));
let sec = match s2k_usage {
0 => {
let sec = php_try!(
crypto::mpis::SecretKey::parse(pk_algo, &mut php));
let their_chksum = php_try!(php.parse_be_u16("checksum"));
let mut cur = Cursor::new(Vec::default());
sec.serialize(&mut cur)?;
let our_chksum: usize = cur.into_inner()
.into_iter().map(|x| x as usize).sum();
if our_chksum as u16 & 0xffff != their_chksum {
return php.fail("wrong secret key checksum");
}
SecretKey::Unencrypted{ mpis: sec }
}
1...253 => {
return php.fail("unsupported secret key encryption");
}
254 => {
let sk: SymmetricAlgorithm = php_try!(php.parse_u8("symm_algo")).into();
let s2k = php_try!(S2K::parse(&mut php));
let mut cipher = php_try!(php.parse_bytes_eof("encrypted_mpis"));
SecretKey::Encrypted{
s2k: s2k,
algorithm: sk,
ciphertext: cipher.into_boxed_slice(),
}
}
255 => {
return php.fail("unsupported secret key encryption");
}
_ => unreachable!()
};
Some(sec)
} else if tag == Tag::PublicKey || tag == Tag::PublicSubkey {
None
} else {
unimplemented!()
};
let key = php_try!(Key::new(time::Tm::from_pgp(creation_time),
pk_algo, mpis, secret));
let tag = php.header.ctb.tag;
php.ok(match tag {
Tag::PublicKey => Packet::PublicKey(key),
Tag::PublicSubkey => Packet::PublicSubkey(key),
Tag::SecretKey => Packet::SecretKey(key),
Tag::SecretSubkey => Packet::SecretSubkey(key),
_ => unreachable!(),
})
}
fn plausible(bio: &mut buffered_reader::Dup<Cookie>, header: &Header) -> Result<()> {
if let BodyLength::Full(len) = header.length {
if len < 6 {
return Err(Error::MalformedPacket(
format!("Packet too short ({} bytes)", len).into()).into());
}
} else {
return Err(
Error::MalformedPacket(
format!("Unexpected body length encoding: {:?}",
header.length)
.into()).into());
}
let data = bio.data(6)?;
if data.len() < 6 {
return Err(
Error::MalformedPacket("Short read".into()).into());
}
let version = data[0];
let pk_algo : PublicKeyAlgorithm = data[5].into();
if version == 4
&& !destructures_to!(PublicKeyAlgorithm::Unknown(_) = pk_algo)
{
Ok(())
} else {
Err(Error::MalformedPacket("Invalid or unsupported data".into())
.into())
}
}
}
impl<'a> Parse<'a, Key> for Key {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParserBuilder::from_reader(reader)?
.buffer_unread_content().finalize()?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::PublicKey(o), PacketParserResult::EOF(_))
| (Packet::PublicSubkey(o), PacketParserResult::EOF(_))
| (Packet::SecretKey(o), PacketParserResult::EOF(_))
| (Packet::SecretSubkey(o), PacketParserResult::EOF(_)) =>
Ok(o),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a Key packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
impl UserID {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
make_php_try!(php);
let value = php_try!(php.parse_bytes_eof("value"));
php.ok(Packet::UserID(UserID::from(value)))
}
}
impl<'a> Parse<'a, UserID> for UserID {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParser::from_reader(reader)?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::UserID(u), PacketParserResult::EOF(_)) =>
Ok(u),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a UserID packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
impl UserAttribute {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
make_php_try!(php);
let value = php_try!(php.parse_bytes_eof("value"));
php.ok(Packet::UserAttribute(UserAttribute::from(value)))
}
}
impl<'a> Parse<'a, UserAttribute> for UserAttribute {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParser::from_reader(reader)?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::UserAttribute(u), PacketParserResult::EOF(_)) =>
Ok(u),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a UserAttribute packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
impl Literal {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>>
{
make_php_try!(php);
let format = php_try!(php.parse_u8("format"));
let filename_len = php_try!(php.parse_u8("filename_len"));
let filename = if filename_len > 0 {
Some(php_try!(php.parse_bytes("filename", filename_len as usize)))
} else {
None
};
let date = php_try!(php.parse_be_u32("date"));
let recursion_depth = php.recursion_depth();
let mut literal = Literal::new(format.into());
if let Some(filename) = filename {
literal.set_filename_from_bytes(&filename)
.expect("length checked above");
}
literal.set_date(Some(time::Tm::from_pgp(date)));
let mut pp = php.ok(Packet::Literal(literal))?;
Cookie::hashing(pp.mut_reader(), Hashing::Enabled,
recursion_depth - 1);
Ok(pp)
}
}
impl<'a> Parse<'a, Literal> for Literal {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParserBuilder::from_reader(reader)?
.buffer_unread_content().finalize()?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::Literal(u), PacketParserResult::EOF(_)) =>
Ok(u),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a Literal packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
#[test]
fn literal_parser_test () {
use constants::DataFormat;
{
let data = bytes!("literal-mode-b.gpg");
let mut pp = PacketParser::from_bytes(data).unwrap().unwrap();
assert_eq!(pp.header.length, BodyLength::Full(18));
let content = pp.steal_eof().unwrap();
let p = pp.finish().unwrap();
if let &Packet::Literal(ref p) = p {
assert_eq!(p.format(), DataFormat::Binary);
assert_eq!(p.filename().unwrap()[..], b"foobar"[..]);
assert_eq!(p.date(), Some(&time::Tm::from_pgp(1507458744)));
assert_eq!(content, b"FOOBAR");
} else {
panic!("Wrong packet!");
}
}
{
let data = bytes!("literal-mode-t-partial-body.gpg");
let mut pp = PacketParser::from_bytes(data).unwrap().unwrap();
assert_eq!(pp.header.length, BodyLength::Partial(4096));
let content = pp.steal_eof().unwrap();
let p = pp.finish().unwrap();
if let &Packet::Literal(ref p) = p {
assert_eq!(p.format(), DataFormat::Text);
assert_eq!(p.filename().unwrap()[..],
b"manifesto.txt"[..]);
assert_eq!(p.date(), Some(&time::Tm::from_pgp(1508000649)));
let expected = bytes!("a-cypherpunks-manifesto.txt");
assert_eq!(&content[..], &expected[..]);
} else {
panic!("Wrong packet!");
}
}
}
impl CompressedData {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
let indent = php.recursion_depth();
tracer!(TRACE, "CompressedData::parse", indent);
make_php_try!(php);
let algo: CompressionAlgorithm =
php_try!(php.parse_u8("algo")).into();
t!("Adding decompressor, recursion depth = {:?}.",
php.recursion_depth());
#[allow(unreachable_patterns)]
match algo {
CompressionAlgorithm::Uncompressed => (),
#[cfg(feature = "compression-deflate")]
CompressionAlgorithm::Zip
| CompressionAlgorithm::Zlib => (),
#[cfg(feature = "compression-bzip2")]
CompressionAlgorithm::BZip2 => (),
CompressionAlgorithm::Unknown(_)
| CompressionAlgorithm::Private(_) =>
return php.fail("unknown compression algorithm"),
_ =>
return php.fail("unsupported compression algorithm"),
}
let recursion_depth = php.recursion_depth();
let mut pp = php.ok(Packet::CompressedData(CompressedData::new(algo)))?;
let reader = pp.take_reader();
let reader = match algo {
CompressionAlgorithm::Uncompressed => {
if TRACE {
eprintln!("CompressedData::parse(): Actually, no need \
for a compression filter: this is an \
\"uncompressed compression packet\".");
}
let _ = recursion_depth;
reader
},
#[cfg(feature = "compression-deflate")]
CompressionAlgorithm::Zip =>
Box::new(buffered_reader::Deflate::with_cookie(
reader, Cookie::new(recursion_depth))),
#[cfg(feature = "compression-deflate")]
CompressionAlgorithm::Zlib =>
Box::new(buffered_reader::Zlib::with_cookie(
reader, Cookie::new(recursion_depth))),
#[cfg(feature = "compression-bzip2")]
CompressionAlgorithm::BZip2 =>
Box::new(buffered_reader::Bzip::with_cookie(
reader, Cookie::new(recursion_depth))),
_ => unreachable!(), };
pp.set_reader(reader);
Ok(pp)
}
}
impl<'a> Parse<'a, CompressedData> for CompressedData {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParserBuilder::from_reader(reader)?
.buffer_unread_content().finalize()?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::CompressedData(u), PacketParserResult::EOF(_)) =>
Ok(u),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a CompressedData packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
#[cfg(any(feature = "compression-deflate", feature = "compression-bzip2"))]
#[test]
fn compressed_data_parser_test () {
use constants::DataFormat;
let expected = bytes!("a-cypherpunks-manifesto.txt");
for i in 1..4 {
match CompressionAlgorithm::from(i) {
#[cfg(feature = "compression-deflate")]
CompressionAlgorithm::Zip | CompressionAlgorithm::Zlib => (),
#[cfg(feature = "compression-bzip2")]
CompressionAlgorithm::BZip2 => (),
_ => continue,
}
let path = path_to(&format!("compressed-data-algo-{}.gpg", i)[..]);
let mut pp = PacketParser::from_file(path).unwrap().unwrap();
if let Packet::CompressedData(ref compressed) = pp.packet {
assert_eq!(compressed.algorithm(), i.into());
} else {
panic!("Wrong packet!");
}
let ppr = pp.recurse().unwrap().1;
let mut pp = ppr.unwrap();
assert_eq!(pp.recursion_depth(), 1);
let content = pp.steal_eof().unwrap();
let (literal, ppr) = pp.recurse().unwrap();
if let Packet::Literal(literal) = literal {
assert_eq!(literal.filename(), None);
assert_eq!(literal.format(), DataFormat::Binary);
assert_eq!(literal.date(), Some(&time::Tm::from_pgp(1509219866)));
assert_eq!(content, expected.to_vec());
} else {
panic!("Wrong packet!");
}
assert!(ppr.is_none());
}
}
impl SKESK {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
use serialize::SerializeInto;
make_php_try!(php);
let version = php_try!(php.parse_u8("version"));
let skesk = match version {
4 => {
let symm_algo = php_try!(php.parse_u8("symm_algo"));
let s2k = php_try!(S2K::parse(&mut php));
let esk = php_try!(php.parse_bytes_eof("esk"));
SKESK::V4(php_try!(SKESK4::new(
version,
symm_algo.into(),
s2k,
if esk.len() > 0 { Some(esk) } else { None },
)))
},
5 => {
let symm_algo: SymmetricAlgorithm =
php_try!(php.parse_u8("symm_algo")).into();
let aead_algo: AEADAlgorithm =
php_try!(php.parse_u8("aead_algo")).into();
let s2k = php_try!(S2K::parse(&mut php));
let iv_size = php_try!(aead_algo.iv_size());
let digest_size = php_try!(aead_algo.digest_size());
let aead_iv = php_try!(php.parse_bytes("aead_iv", iv_size));
let body_length = match php.header.length {
BodyLength::Full(l) => l as usize,
_ => return Err(Error::MalformedPacket(
"SKESK packet must not use partial body or \
indeterminate length".into()).into()),
};
let esk_size = body_length
- 1 - 1 - 1 - s2k.serialized_len() - iv_size - digest_size;
let esk = php_try!(php.parse_bytes("esk", esk_size));
let aead_digest =
php_try!(php.parse_bytes("aead_digest", digest_size));
SKESK::V5(php_try!(SKESK5::new(
version,
symm_algo,
aead_algo,
s2k,
aead_iv.into_boxed_slice(),
esk,
aead_digest.into_boxed_slice(),
)))
},
_ => {
return php.fail("unknown version");
}
};
php.ok(Packet::SKESK(skesk))
}
}
impl<'a> Parse<'a, SKESK> for SKESK {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParser::from_reader(reader)?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::SKESK(u), PacketParserResult::EOF(_)) =>
Ok(u),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a SKESK packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
#[test]
fn skesk_parser_test() {
use crypto::Password;
struct Test<'a> {
filename: &'a str,
s2k: S2K,
cipher_algo: SymmetricAlgorithm,
password: Password,
key_hex: &'a str,
};
let tests = [
Test {
filename: "s2k/mode-3-encrypted-key-password-bgtyhn.gpg",
cipher_algo: SymmetricAlgorithm::AES128,
s2k: S2K::Iterated {
hash: HashAlgorithm::SHA1,
salt: [0x82, 0x59, 0xa0, 0x6e, 0x98, 0xda, 0x94, 0x1c],
iterations: S2K::decode_count(238),
},
password: "bgtyhn".into(),
key_hex: "474E5C373BA18AF0A499FCAFE6093F131DF636F6A3812B9A8AE707F1F0214AE9",
},
];
for test in tests.iter() {
let path = path_to(test.filename);
let mut pp = PacketParser::from_file(path).unwrap().unwrap();
if let Packet::SKESK(SKESK::V4(ref skesk)) = pp.packet {
eprintln!("{:?}", skesk);
assert_eq!(skesk.symmetric_algo(), test.cipher_algo);
assert_eq!(skesk.s2k(), &test.s2k);
match skesk.decrypt(&test.password) {
Ok((_symm_algo, key)) => {
let key = ::conversions::to_hex(&key[..], false);
assert_eq!(&key[..], &test.key_hex[..]);
}
Err(e) => {
panic!("No session key, got: {:?}", e);
}
}
} else {
panic!("Wrong packet!");
}
}
}
impl SEIP {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
make_php_try!(php);
let version = php_try!(php.parse_u8("version"));
if version != 1 {
return php.fail("unknown version");
}
php.ok(Packet::SEIP(SEIP::new()))
.map(|pp| pp.set_decrypted(false))
}
}
impl<'a> Parse<'a, SEIP> for SEIP {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParser::from_reader(reader)?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::SEIP(u), PacketParserResult::EOF(_)) =>
Ok(u),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a SEIP packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
impl MDC {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
make_php_try!(php);
let mut computed_hash : [u8; 20] = Default::default();
{
let mut r : Option<&mut BufferedReader<Cookie>>
= Some(&mut php.reader);
while let Some(bio) = r {
{
let state = bio.cookie_mut();
if state.hashes_for == HashesFor::MDC {
if state.sig_group().hashes.len() > 0 {
let mut h = state.sig_group_mut().hashes
.get_mut(&HashAlgorithm::SHA1)
.unwrap();
h.digest(&mut computed_hash);
}
break;
}
}
r = bio.get_mut();
}
}
let mut hash : [u8; 20] = Default::default();
hash.copy_from_slice(&php_try!(php.parse_bytes("hash", 20)));
php.ok(Packet::MDC(MDC::new(hash, computed_hash)))
}
}
impl<'a> Parse<'a, MDC> for MDC {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParser::from_reader(reader)?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::MDC(u), PacketParserResult::EOF(_)) =>
Ok(u),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a MDC packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
impl AED {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
make_php_try!(php);
let version = php_try!(php.parse_u8("version"));
if version != 1 {
return php.fail("unknown version");
}
let cipher: SymmetricAlgorithm =
php_try!(php.parse_u8("symm_algo")).into();
let aead: AEADAlgorithm =
php_try!(php.parse_u8("aead_algo")).into();
let chunk_size: usize =
1 << (php_try!(php.parse_u8("chunk_size")) as usize + 6);
let iv_size = php_try!(aead.iv_size());
let iv = php_try!(php.parse_bytes("iv", iv_size));
let aed = php_try!(AED::new(
cipher, aead, chunk_size, iv.into_boxed_slice()
));
php.ok(Packet::AED(aed)).map(|pp| pp.set_decrypted(false))
}
}
impl<'a> Parse<'a, AED> for AED {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParser::from_reader(reader)?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::AED(u), PacketParserResult::EOF(_)) =>
Ok(u),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a AED packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
impl MPI {
fn parse<'a>(name: &'static str, php: &mut PacketHeaderParser<'a>) -> Result<Self> {
let bits = php.parse_be_u16("mpi_len")? as usize;
if bits == 0 {
return Ok(MPI{ bits: 0, value: vec![].into_boxed_slice()});
}
let bytes = (bits + 7) / 8;
let value = Vec::from(&php.parse_bytes(name, bytes)?[..bytes]);
if TRACE {
eprintln!("bits: {}, value: {}",
bits, ::conversions::to_hex(&value, true));
}
let unused_bits = bytes * 8 - bits;
assert_eq!(bytes * 8 - unused_bits, bits);
if TRACE {
eprintln!("unused bits: {}", unused_bits);
}
if unused_bits > 0 {
let mask = !((1 << (8 - unused_bits)) - 1);
let unused_value = value[0] & mask;
if TRACE {
eprintln!("mask: {:08b} & first byte: {:08b} \
= unused value: {:08b}",
mask, value[0], unused_value);
}
if unused_value != 0 {
return Err(Error::MalformedMPI(
format!("{} unused bits not zeroed: ({:x})",
unused_bits, unused_value)).into());
}
}
let first_used_bit = 8 - unused_bits;
if value[0] & (1 << (first_used_bit - 1)) == 0 {
return Err(Error::MalformedMPI(
format!("leading bit is not set: \
expected bit {} to be set in {:8b} ({:x})",
first_used_bit, value[0], value[0])).into());
}
Ok(MPI{
bits: bits,
value: value.into_boxed_slice()
})
}
pub fn decode_point(&self, curve: &Curve) -> Result<(&[u8], &[u8])> {
use nettle::{ed25519, curve25519};
use self::Curve::*;
match &curve {
Ed25519 | Cv25519 => {
assert_eq!(curve25519::CURVE25519_SIZE,
ed25519::ED25519_KEY_SIZE);
if self.value.len() != 1 + curve25519::CURVE25519_SIZE {
return Err(Error::MalformedPacket(
format!("Bad size of Curve25519 key: {} expected: {}",
self.value.len(),
1 + curve25519::CURVE25519_SIZE)).into());
}
if self.value[0] != 0x40 {
return Err(Error::MalformedPacket(
"Bad encoding of Curve25519 key".into()).into());
}
Ok((&self.value[1..], &[]))
},
_ => {
let coordinate_length = (curve.len()? + 7) / 8;
let expected_length =
1 + (2 * coordinate_length);
if self.value.len() != expected_length {
return Err(Error::InvalidArgument(
format!("Invalid length of MPI: {} (expected {})",
self.value.len(), expected_length)).into());
}
if self.value[0] != 0x04 {
return Err(Error::InvalidArgument(
format!("Bad prefix: {:x} (expected 0x04)", self.value[0]))
.into());
}
Ok((&self.value[1..1 + coordinate_length],
&self.value[1 + coordinate_length..]))
},
}
}
}
impl<'a> Parse<'a, MPI> for MPI {
fn from_reader<R: io::Read>(reader: R) -> Result<Self> {
let bio = buffered_reader::Generic::with_cookie(
reader, None, Cookie::default());
let mut parser = PacketHeaderParser::new_naked(Box::new(bio));
Self::parse("(none)", &mut parser)
}
}
impl PKESK {
fn parse<'a>(mut php: PacketHeaderParser<'a>) -> Result<PacketParser<'a>> {
make_php_try!(php);
let version = php_try!(php.parse_u8("version"));
if version != 3 {
return php.fail("unknown version");
}
let mut keyid = [0u8; 8];
keyid.copy_from_slice(&php_try!(php.parse_bytes("keyid", 8)));
let pk_algo: PublicKeyAlgorithm = php_try!(php.parse_u8("pk_algo")).into();
if ! pk_algo.can_encrypt() {
return php.fail("not an encryption algorithm");
}
let mpis = crypto::mpis::Ciphertext::parse(pk_algo, &mut php)?;
let pkesk = php_try!(PKESK::new(KeyID::from_bytes(&keyid),
pk_algo, mpis));
php.ok(Packet::PKESK(pkesk))
}
}
impl<'a> Parse<'a, PKESK> for PKESK {
fn from_reader<R: 'a + Read>(reader: R) -> Result<Self> {
let ppr = PacketParserBuilder::from_reader(reader)?
.buffer_unread_content().finalize()?;
let (p, ppr) = match ppr {
PacketParserResult::Some(mut pp) => {
pp.next()?
},
PacketParserResult::EOF(_) =>
return Err(Error::InvalidOperation(
"Unexpected EOF".into()).into()),
};
match (p, ppr) {
(Packet::PKESK(o), PacketParserResult::EOF(_)) =>
Ok(o),
(p, PacketParserResult::EOF(_)) =>
Err(Error::InvalidOperation(
format!("Not a PKESK packet: {:?}", p)).into()),
(_, PacketParserResult::Some(_)) =>
Err(Error::InvalidOperation(
"Excess data after packet".into()).into()),
}
}
}
#[derive(Debug)]
struct PacketParserState {
settings: PacketParserSettings,
message_validator: MessageValidator,
keyring_validator: ::tpk::KeyringValidator,
tpk_validator: ::tpk::TPKValidator,
first_packet: bool,
}
impl PacketParserState {
fn new(settings: PacketParserSettings) -> Self {
PacketParserState {
settings: settings,
message_validator: Default::default(),
keyring_validator: Default::default(),
tpk_validator: Default::default(),
first_packet: true,
}
}
}
pub struct PacketParser<'a> {
header: Header,
pub packet: Packet,
path: Vec<usize>,
last_path: Vec<usize>,
reader: Box<BufferedReader<Cookie> + 'a>,
content_was_read: bool,
finished: bool,
decrypted: bool,
map: Option<map::Map>,
state: PacketParserState,
}
impl<'a> std::fmt::Display for PacketParser<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "PacketParser")
}
}
impl<'a> std::fmt::Debug for PacketParser<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("PacketParser")
.field("header", &self.header)
.field("packet", &self.packet)
.field("path", &self.path)
.field("last_path", &self.last_path)
.field("decrypted", &self.decrypted)
.field("content_was_read", &self.content_was_read)
.field("settings", &self.state.settings)
.field("map", &self.map)
.finish()
}
}
enum ParserResult<'a> {
Success(PacketParser<'a>),
EOF((Box<BufferedReader<Cookie> + 'a>, PacketParserState, Vec<usize>)),
}
#[derive(Debug)]
pub struct PacketParserEOF {
state: PacketParserState,
last_path: Vec<usize>,
}
impl PacketParserEOF {
fn new(mut state: PacketParserState) -> Self {
state.message_validator.finish();
state.keyring_validator.finish();
state.tpk_validator.finish();
PacketParserEOF {
state: state,
last_path: vec![],
}
}
pub fn is_message(&self) -> bool {
self.state.message_validator.is_message()
}
pub fn is_keyring(&self) -> bool {
self.state.keyring_validator.is_keyring()
}
pub fn is_tpk(&self) -> bool {
self.state.tpk_validator.is_tpk()
}
pub fn last_path(&self) -> &[usize] {
&self.last_path[..]
}
pub fn last_recursion_depth(&self) -> Option<isize> {
if self.last_path.len() == 0 {
None
} else {
Some(self.last_path.len() as isize - 1)
}
}
}
#[derive(Debug)]
pub enum PacketParserResult<'a> {
Some(PacketParser<'a>),
EOF(PacketParserEOF),
}
impl<'a> PacketParserResult<'a> {
pub fn is_none(&self) -> bool {
if let PacketParserResult::EOF(_) = self {
true
} else {
false
}
}
pub fn is_eof(&self) -> bool {
Self::is_none(self)
}
pub fn is_some(&self) -> bool {
! Self::is_none(self)
}
pub fn expect(self, msg: &str) -> PacketParser<'a> {
if let PacketParserResult::Some(pp) = self {
return pp;
} else {
panic!("{}", msg);
}
}
pub fn unwrap(self) -> PacketParser<'a> {
self.expect("called `PacketParserResult::unwrap()` on a \
`PacketParserResult::PacketParserEOF` value")
}
pub fn as_ref(&self) -> Option<&PacketParser<'a>> {
if let PacketParserResult::Some(ref pp) = self {
Some(pp)
} else {
None
}
}
pub fn as_mut(&mut self) -> Option<&mut PacketParser<'a>> {
if let PacketParserResult::Some(ref mut pp) = self {
Some(pp)
} else {
None
}
}
pub fn take(&mut self) -> Self {
mem::replace(
self,
PacketParserResult::EOF(
PacketParserEOF::new(
PacketParserState::new(Default::default()))))
}
pub fn map<U, F>(self, f: F) -> Option<U>
where F: FnOnce(PacketParser<'a>) -> U
{
match self {
PacketParserResult::Some(x) => Some(f(x)),
PacketParserResult::EOF(_) => None,
}
}
pub fn recursion_depth(&self) -> Option<isize> {
match self {
PacketParserResult::Some(pp) => Some(pp.recursion_depth()),
PacketParserResult::EOF(_) => None,
}
}
pub fn last_recursion_depth(&self) -> Option<isize> {
match self {
PacketParserResult::Some(pp) => pp.last_recursion_depth(),
PacketParserResult::EOF(eof) => eof.last_recursion_depth(),
}
}
}
impl<'a> Parse<'a, PacketParserResult<'a>> for PacketParser<'a> {
fn from_reader<R: io::Read + 'a>(reader: R)
-> Result<PacketParserResult<'a>> {
PacketParserBuilder::from_reader(reader)?.finalize()
}
fn from_file<P: AsRef<Path>>(path: P)
-> Result<PacketParserResult<'a>> {
PacketParserBuilder::from_file(path)?.finalize()
}
fn from_bytes(bytes: &'a [u8])
-> Result<PacketParserResult<'a>> {
PacketParserBuilder::from_bytes(bytes)?.finalize()
}
}
impl <'a> PacketParser<'a> {
pub(crate) fn from_buffered_reader(bio: Box<BufferedReader<Cookie> + 'a>)
-> Result<PacketParserResult<'a>> {
PacketParserBuilder::from_buffered_reader(bio)?.finalize()
}
fn take_reader(&mut self) -> Box<BufferedReader<Cookie> + 'a> {
self.set_reader(
Box::new(buffered_reader::EOF::with_cookie(Default::default())))
}
fn set_reader(&mut self, reader: Box<BufferedReader<Cookie> + 'a>)
-> Box<BufferedReader<Cookie> + 'a>
{
mem::replace(&mut self.reader, reader)
}
fn mut_reader(&mut self) -> &mut BufferedReader<Cookie> {
&mut self.reader
}
fn set_decrypted(mut self, v: bool) -> Self {
self.decrypted = v;
self
}
pub fn decrypted(&self) -> bool {
self.decrypted
}
pub fn last_path(&self) -> &[usize] {
&self.last_path[..]
}
pub fn path(&self) -> &[usize] {
&self.path[..]
}
pub fn recursion_depth(&self) -> isize {
self.path.len() as isize - 1
}
pub fn last_recursion_depth(&self) -> Option<isize> {
if self.last_path.len() == 0 {
assert_eq!(&self.path[..], &[ 0 ]);
None
} else {
Some(self.last_path.len() as isize - 1)
}
}
pub fn possible_message(&self) -> bool {
self.state.message_validator.check().is_message_prefix()
}
pub fn possible_keyring(&self) -> bool {
self.state.keyring_validator.check().is_keyring_prefix()
}
pub fn possible_tpk(&self) -> bool {
self.state.tpk_validator.check().is_tpk_prefix()
}
fn plausible(mut bio: &mut buffered_reader::Dup<Cookie>, header: &Header) -> Result<()> {
let bad = Err(
Error::MalformedPacket("Can't make an educated case".into()).into());
match header.ctb.tag {
Tag::Reserved | Tag::Marker
| Tag::Unknown(_) | Tag::Private(_) =>
Err(Error::MalformedPacket("Looks like garbage".into()).into()),
Tag::Signature => Signature::plausible(&mut bio, &header),
Tag::SecretKey => Key::plausible(&mut bio, &header),
Tag::PublicKey => Key::plausible(&mut bio, &header),
Tag::SecretSubkey => Key::plausible(&mut bio, &header),
Tag::PublicSubkey => Key::plausible(&mut bio, &header),
Tag::UserID => bad,
Tag::UserAttribute => bad,
Tag::PKESK => bad,
Tag::SKESK => bad,
Tag::OnePassSig => bad,
Tag::CompressedData => bad,
Tag::SED => bad,
Tag::Literal => bad,
Tag::Trust => bad,
Tag::SEIP => bad,
Tag::MDC => bad,
Tag::AED => bad,
}
}
fn parse(mut bio: Box<BufferedReader<Cookie> + 'a>,
state: PacketParserState,
path: Vec<usize>)
-> Result<ParserResult<'a>>
{
assert!(path.len() > 0);
let indent = path.len() as isize - 1;
tracer!(TRACE, "PacketParser::parse", indent);
t!("Parsing packet at {:?}", path);
let recursion_depth = path.len() as isize - 1;
if bio.data(1)?.len() == 0 {
t!("No packet at {:?} (EOF).", path);
return Ok(ParserResult::EOF((bio, state, path)));
}
let mut bio = buffered_reader::Dup::with_cookie(bio, Cookie::default());
let mut header;
let mut skip = 0;
let mut orig_error : Option<failure::Error> = None;
loop {
bio.rewind();
bio.data_consume_hard(skip)?;
match Header::parse(&mut bio) {
Ok(header_) => {
if skip == 0 {
header = header_;
break;
}
match Self::plausible(&mut bio, &header_) {
Ok(()) => {
header = header_;
break;
}
Err(_err) => (),
}
}
Err(err) => {
if orig_error.is_none() {
orig_error = Some(err.into());
}
if state.first_packet || skip > 32 * 1024 {
return Err(orig_error.unwrap());
}
}
}
skip = skip + 1;
}
let consumed = if skip == 0 {
bio.total_out()
} else {
t!("turning {} bytes of junk into an Unknown packet", skip);
header = Header {
ctb: CTB::new(Tag::Reserved),
length: BodyLength::Full(skip as u32),
};
0
};
let tag = header.ctb.tag;
let mut bio = Box::new(bio).into_inner().unwrap();
if tag == Tag::Literal {
Cookie::hashing(
&mut bio, Hashing::Disabled, recursion_depth - 1);
} else if tag == Tag::OnePassSig || tag == Tag::Signature {
Cookie::hashing(
&mut bio, Hashing::Notarized, recursion_depth - 1);
}
let header_bytes =
Vec::from(&bio.data_consume_hard(consumed)?[..consumed]);
let bio : Box<BufferedReader<Cookie>>
= match header.length {
BodyLength::Full(len) => {
t!("Pushing a limitor ({} bytes), level: {}.",
len, recursion_depth);
Box::new(buffered_reader::Limitor::with_cookie(
bio, len as u64,
Cookie::new(recursion_depth)))
},
BodyLength::Partial(len) => {
t!("Pushing a partial body chunk decoder, level: {}.",
recursion_depth);
Box::new(BufferedReaderPartialBodyFilter::with_cookie(
bio, len,
tag != Tag::Literal,
Cookie::new(recursion_depth)))
},
BodyLength::Indeterminate => {
t!("Indeterminate length packet, not adding a limitor.");
bio
},
};
let parser = PacketHeaderParser::new(bio, state, path,
header, header_bytes);
let mut result = match tag {
Tag::Signature => Signature::parse(parser),
Tag::OnePassSig => OnePassSig::parse(parser),
Tag::PublicSubkey => Key::parse(parser),
Tag::PublicKey => Key::parse(parser),
Tag::SecretKey => Key::parse(parser),
Tag::SecretSubkey => Key::parse(parser),
Tag::UserID => UserID::parse(parser),
Tag::UserAttribute => UserAttribute::parse(parser),
Tag::Literal => Literal::parse(parser),
Tag::CompressedData => CompressedData::parse(parser),
Tag::SKESK => SKESK::parse(parser),
Tag::SEIP => SEIP::parse(parser),
Tag::MDC => MDC::parse(parser),
Tag::PKESK => PKESK::parse(parser),
Tag::AED => AED::parse(parser),
Tag::Reserved if skip > 0 => Unknown::parse(
parser, Error::MalformedPacket(format!(
"Skipped {} bytes of junk", skip)).into()),
_ => Unknown::parse(parser,
Error::UnsupportedPacketType(tag).into()),
}?;
if tag == Tag::OnePassSig {
Cookie::hashing(
&mut result, Hashing::Enabled, recursion_depth - 1);
}
result.state.first_packet = false;
t!(" -> {:?}, path: {:?}, level: {:?}.",
result.packet.tag(), result.path, result.cookie_ref().level);
return Ok(ParserResult::Success(result));
}
pub fn next(mut self)
-> Result<(Packet, PacketParserResult<'a>)>
{
let indent = self.recursion_depth();
tracer!(TRACE, "PacketParser::next", indent);
t!("({:?}, path: {:?}, level: {:?}).",
self.packet.tag(), self.path, self.cookie_ref().level);
self.finish()?;
let (mut fake_eof, mut reader) = buffered_reader_stack_pop(
mem::replace(&mut self.reader,
Box::new(buffered_reader::EOF::with_cookie(
Default::default()))),
self.recursion_depth())?;
assert!(! fake_eof);
self.last_path.clear();
self.last_path.extend_from_slice(&self.path[..]);
*self.path.last_mut().expect("A path is never empty") += 1;
loop {
t!("Reading packet at {:?} from: {:?}", self.path, reader);
let recursion_depth = self.recursion_depth();
let ppr = PacketParser::parse(reader, self.state, self.path)?;
match ppr {
ParserResult::EOF((reader_, state_, path_)) => {
t!("depth: {}, got EOF trying to read the next packet",
recursion_depth);
self.path = path_;
if ! fake_eof && recursion_depth == 0 {
t!("Popped top-level container, done reading message.");
let mut eof = PacketParserEOF::new(state_);
eof.last_path = self.last_path;
return Ok((self.packet,
PacketParserResult::EOF(eof)));
} else {
self.state = state_;
self.finish()?;
let (fake_eof_, reader_) = buffered_reader_stack_pop(
reader_, recursion_depth - 1)?;
fake_eof = fake_eof_;
if ! fake_eof {
self.path.pop().unwrap();
*self.path.last_mut()
.expect("A path is never empty") += 1;
}
reader = reader_;
}
},
ParserResult::Success(mut pp) => {
pp.state.message_validator.push(
pp.packet.tag(), recursion_depth);
pp.state.keyring_validator.push(pp.packet.tag());
pp.state.tpk_validator.push(pp.packet.tag());
pp.last_path = self.last_path;
return Ok((self.packet, PacketParserResult::Some(pp)));
}
}
}
}
pub fn recurse(self) -> Result<(Packet, PacketParserResult<'a>)> {
let indent = self.recursion_depth();
tracer!(TRACE, "PacketParser::recurse", indent);
t!("({:?}, path: {:?}, level: {:?})",
self.packet.tag(), self.path, self.cookie_ref().level);
match self.packet {
Packet::CompressedData(_) | Packet::SEIP(_) | Packet::AED(_)
if self.decrypted =>
{
if self.recursion_depth() as u8
>= self.state.settings.max_recursion_depth
{
t!("Not recursing into the {:?} packet, maximum recursion \
depth ({}) reached.",
self.packet.tag(),
self.state.settings.max_recursion_depth);
} else if self.content_was_read {
t!("Not recursing into the {:?} packet, some data was \
already read.",
self.packet.tag());
} else {
let mut last_path = self.last_path;
last_path.clear();
last_path.extend_from_slice(&self.path[..]);
let mut path = self.path;
path.push(0);
let recursion_depth = path.len() as isize - 1;
match PacketParser::parse(self.reader, self.state, path)? {
ParserResult::Success(mut pp) => {
t!("Recursed into the {:?} packet, got a {:?}.",
self.packet.tag(), pp.packet.tag());
pp.state.message_validator.push(
pp.packet.tag(),
recursion_depth);
pp.state.keyring_validator.push(pp.packet.tag());
pp.state.tpk_validator.push(pp.packet.tag());
pp.last_path = last_path;
return Ok((self.packet,
PacketParserResult::Some(pp)));
},
ParserResult::EOF(_) => {
return Err(Error::MalformedPacket(
"Container is truncated".into()).into());
},
}
}
},
Packet::CompressedData(_) => unreachable!(),
Packet::Unknown(_) | Packet::Signature(_) | Packet::OnePassSig(_)
| Packet::PublicKey(_) | Packet::PublicSubkey(_)
| Packet::SecretKey(_) | Packet::SecretSubkey(_)
| Packet::UserID(_) | Packet::UserAttribute(_)
| Packet::Literal(_) | Packet::PKESK(_) | Packet::SKESK(_)
| Packet::SEIP(_) | Packet::MDC(_) | Packet::AED(_) => {
t!("A {:?} packet is not a container, not recursing.",
self.packet.tag());
},
}
self.next()
}
pub fn buffer_unread_content(&mut self) -> Result<&[u8]> {
let mut rest = self.steal_eof()?;
if rest.len() > 0 {
if let Some(mut body) = self.packet.body.take() {
body.append(&mut rest);
self.packet.body = Some(body);
} else {
self.packet.body = Some(rest);
}
Ok(&self.packet.body.as_ref().unwrap()[..])
} else {
Ok(&b""[..])
}
}
pub fn finish<'b>(&'b mut self) -> Result<&'b Packet> {
let indent = self.recursion_depth();
tracer!(TRACE, "PacketParser::finish", indent);
if self.finished {
return Ok(&mut self.packet);
}
let recursion_depth = self.recursion_depth();
let unread_content = if self.state.settings.buffer_unread_content {
t!("({:?} at depth {}): buffering {} bytes of unread content",
self.packet.tag(), recursion_depth,
self.data_eof().unwrap().len());
self.buffer_unread_content()?.len() > 0
} else {
t!("{:?} at depth {}): dropping {} bytes of unread content",
self.packet.tag(), recursion_depth,
self.data_eof().unwrap().len());
self.drop_eof()?
};
if unread_content {
match self.packet.tag() {
Tag::SEIP | Tag::AED | Tag::SED | Tag::CompressedData => {
self.state.message_validator.push_token(
message::Token::OpaqueContent,
recursion_depth + 1);
}
_ => {},
}
}
self.finished = true;
Ok(&mut self.packet)
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn map(&self) -> Option<&map::Map> {
self.map.as_ref()
}
pub fn take_map(&mut self) -> Option<map::Map> {
self.map.take()
}
}
impl<'a> io::Read for PacketParser<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.content_was_read = true;
return buffered_reader_generic_read_impl(self, buf);
}
}
impl<'a> BufferedReader<Cookie> for PacketParser<'a> {
fn buffer(&self) -> &[u8] {
self.reader.buffer()
}
fn data(&mut self, amount: usize) -> io::Result<&[u8]> {
self.reader.data(amount)
}
fn data_hard(&mut self, amount: usize) -> io::Result<&[u8]> {
self.reader.data_hard(amount)
}
fn data_eof(&mut self) -> io::Result<&[u8]> {
self.reader.data_eof()
}
fn consume(&mut self, amount: usize) -> &[u8] {
self.content_was_read |= amount > 0;
self.reader.consume(amount)
}
fn data_consume(&mut self, amount: usize) -> io::Result<&[u8]> {
self.content_was_read |= amount > 0;
self.reader.data_consume(amount)
}
fn data_consume_hard(&mut self, amount: usize) -> io::Result<&[u8]> {
self.content_was_read |= amount > 0;
self.reader.data_consume_hard(amount)
}
fn read_be_u16(&mut self) -> io::Result<u16> {
self.content_was_read = true;
self.reader.read_be_u16()
}
fn read_be_u32(&mut self) -> io::Result<u32> {
self.content_was_read = true;
self.reader.read_be_u32()
}
fn steal(&mut self, amount: usize) -> io::Result<Vec<u8>> {
self.content_was_read |= amount > 0;
self.reader.steal(amount)
}
fn steal_eof(&mut self) -> io::Result<Vec<u8>> {
self.content_was_read = true;
self.reader.steal_eof()
}
fn drop_eof(&mut self) -> io::Result<bool> {
self.content_was_read = true;
self.reader.drop_eof()
}
fn get_mut(&mut self) -> Option<&mut BufferedReader<Cookie>> {
None
}
fn get_ref(&self) -> Option<&BufferedReader<Cookie>> {
None
}
fn into_inner<'b>(self: Box<Self>)
-> Option<Box<BufferedReader<Cookie> + 'b>>
where Self: 'b {
None
}
fn cookie_set(&mut self, cookie: Cookie)
-> Cookie {
self.reader.cookie_set(cookie)
}
fn cookie_ref(&self) -> &Cookie {
self.reader.cookie_ref()
}
fn cookie_mut(&mut self) -> &mut Cookie {
self.reader.cookie_mut()
}
}
#[cfg(feature = "compression-deflate")]
#[test]
fn packet_parser_reader_interface() {
use std::io::Read;
let expected = bytes!("a-cypherpunks-manifesto.txt");
let path = path_to("compressed-data-algo-1.gpg");
let pp = PacketParser::from_file(path).unwrap().unwrap();
let (packet, ppr) = pp.recurse().unwrap();
let packet_depth = ppr.last_recursion_depth().unwrap();
let pp_depth = ppr.recursion_depth().unwrap();
if let Packet::CompressedData(_) = packet {
} else {
panic!("Expected a compressed data packet.");
}
let relative_position = pp_depth - packet_depth;
assert_eq!(relative_position, 1);
let mut pp = ppr.unwrap();
if let Packet::Literal(_) = pp.packet {
} else {
panic!("Expected a literal data packet.");
}
for i in 0..expected.len() {
let mut buf = [0u8; 1];
let r = pp.read(&mut buf).unwrap();
assert_eq!(r, 1);
assert_eq!(buf[0], expected[i]);
}
let mut buf = [0u8; 1];
let r = pp.read(&mut buf).unwrap();
assert_eq!(r, 0);
let (packet, ppr) = pp.recurse().unwrap();
assert!(ppr.is_none());
assert!(packet.body.is_none());
}
impl<'a> PacketParser<'a> {
pub fn decrypt(&mut self, algo: SymmetricAlgorithm, key: &SessionKey)
-> Result<()>
{
let indent = self.recursion_depth();
tracer!(TRACE, "PacketParser::decrypt", indent);
if self.content_was_read {
return Err(Error::InvalidOperation(
format!("Packet's content has already been read.")).into());
}
if self.decrypted {
return Err(Error::InvalidOperation(
format!("Packet not encrypted.")).into());
}
if algo.key_size()? != key.len () {
return Err(Error::InvalidOperation(
format!("Bad key size: {} expected: {}",
key.len(), algo.key_size()?)).into());
}
match self.packet.clone() {
Packet::SEIP(_) => {
let bl = algo.block_size()?;
{
let mut dec = Decryptor::new(
algo, key, &self.data_hard(bl + 2)?[..bl + 2])?;
let mut header = vec![ 0u8; bl + 2 ];
dec.read(&mut header)?;
if !(header[bl - 2] == header[bl]
&& header[bl - 1] == header[bl + 1]) {
return Err(Error::InvalidSessionKey(
format!(
"Last two 16-bit quantities don't match: {}",
::conversions::to_hex(&header[..], false)))
.into());
}
}
let reader = self.take_reader();
let mut reader = BufferedReaderDecryptor::with_cookie(
algo, key, reader, Cookie::default()).unwrap();
reader.cookie_mut().level = Some(self.recursion_depth());
t!("Pushing Decryptor, level {:?}.", reader.cookie_ref().level);
let mut reader = HashedReader::new(
reader, HashesFor::MDC, vec![HashAlgorithm::SHA1]);
reader.cookie_mut().level = Some(self.recursion_depth());
t!("Pushing HashedReader, level {:?}.",
reader.cookie_ref().level);
let mut reader = buffered_reader::Reserve::with_cookie(
Box::new(reader), 1 + 1 + 20,
Cookie::new(self.recursion_depth()));
reader.cookie_mut().fake_eof = true;
t!("Pushing buffered_reader::Reserve, level: {}.",
self.recursion_depth());
reader.data_consume_hard(bl + 2).unwrap();
self.reader = Box::new(reader);
self.decrypted = true;
Ok(())
},
Packet::AED(aed) => {
{
let data = self.data(aed.chunk_digest_size()?)?;
let mut dec = aead::Decryptor::new(
1, aed.cipher(), aed.aead(), aed.chunk_size(),
aed.iv(), key, &data[..cmp::min(data.len(), aed.chunk_digest_size()?)])?;
let mut chunk = Vec::new();
dec.take(aed.chunk_size() as u64).read_to_end(&mut chunk)?;
}
let reader = self.take_reader();
let mut reader = aead::BufferedReaderDecryptor::with_cookie(
1, aed.cipher(), aed.aead(), aed.chunk_size(),
aed.iv(), key, reader, Cookie::default()).unwrap();
reader.cookie_mut().level = Some(self.recursion_depth());
t!("Pushing aead::Decryptor, level {:?}.",
reader.cookie_ref().level);
self.reader = Box::new(reader);
self.decrypted = true;
Ok(())
},
_ =>
Err(Error::InvalidOperation(
format!("Can't decrypt {:?} packets.",
self.packet.tag())).into())
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::fs::File;
use std::path::PathBuf;
fn path_to(artifact: &str) -> PathBuf {
[env!("CARGO_MANIFEST_DIR"), "tests", "data", "messages", artifact]
.iter().collect()
}
fn path_to_data(artifact: &str) -> PathBuf {
[env!("CARGO_MANIFEST_DIR"), "tests", "data", artifact]
.iter().collect()
}
enum Data<'a> {
File(&'a str),
String(&'a [u8]),
}
impl<'a> Data<'a> {
fn content(&self) -> Vec<u8> {
match self {
Data::File(filename) => {
let path = path_to(filename);
let mut f = File::open(path.clone())
.expect(&format!("Opening '{:?}'", path)[..]);
let mut buffer : Vec<u8> = vec![];
f.read_to_end(&mut buffer)
.expect(&format!("Reading '{:?}'", path)[..]);
buffer
},
Data::String(data) => data.to_vec(),
}
}
}
struct DecryptTest<'a> {
filename: &'a str,
algo: SymmetricAlgorithm,
key_hex: &'a str,
plaintext: Data<'a>,
paths: &'a[ (Tag, &'a[ usize ] ) ],
}
const DECRYPT_TESTS: [DecryptTest; 10] = [
DecryptTest {
filename: "encrypted-aes256-password-123.gpg",
algo: SymmetricAlgorithm::AES256,
key_hex: "7EF4F08C44F780BEA866961423306166B8912C43352F3D9617F745E4E3939710",
plaintext: Data::File("a-cypherpunks-manifesto.txt"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::SEIP, &[ 1 ]),
(Tag::Literal, &[ 1, 0 ]),
(Tag::MDC, &[ 1, 1 ]),
],
},
DecryptTest {
filename: "encrypted-aes192-password-123456.gpg",
algo: SymmetricAlgorithm::AES192,
key_hex: "B2F747F207EFF198A6C826F1D398DE037986218ED468DB61",
plaintext: Data::File("a-cypherpunks-manifesto.txt"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::SEIP, &[ 1 ]),
(Tag::Literal, &[ 1, 0 ]),
(Tag::MDC, &[ 1, 1 ]),
],
},
DecryptTest {
filename: "encrypted-aes128-password-123456789.gpg",
algo: SymmetricAlgorithm::AES128,
key_hex: "AC0553096429260B4A90B1CEC842D6A0",
plaintext: Data::File("a-cypherpunks-manifesto.txt"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::SEIP, &[ 1 ]),
(Tag::Literal, &[ 1, 0 ]),
(Tag::MDC, &[ 1, 1 ]),
],
},
DecryptTest {
filename: "encrypted-twofish-password-red-fish-blue-fish.gpg",
algo: SymmetricAlgorithm::Twofish,
key_hex: "96AFE1EDFA7C9CB7E8B23484C718015E5159CFA268594180D4DB68B2543393CB",
plaintext: Data::File("a-cypherpunks-manifesto.txt"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::SEIP, &[ 1 ]),
(Tag::Literal, &[ 1, 0 ]),
(Tag::MDC, &[ 1, 1 ]),
],
},
DecryptTest {
filename: "seip/msg-compression-not-signed-password-123.pgp",
algo: SymmetricAlgorithm::AES128,
key_hex: "86A8C1C7961F55A3BE181A990D0ABB2A",
plaintext: Data::String(b"compression, not signed\n"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::SEIP, &[ 1 ]),
(Tag::CompressedData, &[ 1, 0 ]),
(Tag::Literal, &[ 1, 0, 0 ]),
(Tag::MDC, &[ 1, 1 ]),
],
},
DecryptTest {
filename: "seip/msg-compression-signed-password-123.pgp",
algo: SymmetricAlgorithm::AES128,
key_hex: "1B195CD35CAD4A99D9399B4CDA4CDA4E",
plaintext: Data::String(b"compression, signed\n"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::SEIP, &[ 1 ]),
(Tag::CompressedData, &[ 1, 0 ]),
(Tag::OnePassSig, &[ 1, 0, 0 ]),
(Tag::Literal, &[ 1, 0, 1 ]),
(Tag::Signature, &[ 1, 0, 2 ]),
(Tag::MDC, &[ 1, 1 ]),
],
},
DecryptTest {
filename: "seip/msg-no-compression-not-signed-password-123.pgp",
algo: SymmetricAlgorithm::AES128,
key_hex: "AFB43B83A4B9D971E4B4A4C53749076A",
plaintext: Data::String(b"no compression, not signed\n"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::SEIP, &[ 1 ]),
(Tag::Literal, &[ 1, 0 ]),
(Tag::MDC, &[ 1, 1 ]),
],
},
DecryptTest {
filename: "seip/msg-no-compression-signed-password-123.pgp",
algo: SymmetricAlgorithm::AES128,
key_hex: "9D5DB92F77F0E4A356EE53813EF2C3DC",
plaintext: Data::String(b"no compression, signed\n"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::SEIP, &[ 1 ]),
(Tag::OnePassSig, &[ 1, 0 ]),
(Tag::Literal, &[ 1, 1 ]),
(Tag::Signature, &[ 1, 2 ]),
(Tag::MDC, &[ 1, 3 ]),
],
},
DecryptTest {
filename: "aed/msg-aes128-eax-chunk-size-64-password-123.pgp",
algo: SymmetricAlgorithm::AES128,
key_hex: "E88151F2B6F6F6F0AE6B56ED247AA61B",
plaintext: Data::File("a-cypherpunks-manifesto.txt"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::AED, &[ 1 ]),
(Tag::Literal, &[ 1, 0 ]),
],
},
DecryptTest {
filename: "aed/msg-aes128-eax-chunk-size-134217728-password-123.pgp",
algo: SymmetricAlgorithm::AES128,
key_hex: "D7EE3F3B049DE011687EC9E08D6DCBB0",
plaintext: Data::File("a-cypherpunks-manifesto.txt"),
paths: &[
(Tag::SKESK, &[ 0 ]),
(Tag::AED, &[ 1 ]),
(Tag::Literal, &[ 1, 0 ]),
],
},
];
fn consume_until<'a>(mut ppr: PacketParserResult<'a>,
ignore_first: bool, keep: &[Tag], skip: &[Tag])
-> PacketParserResult<'a>
{
if ignore_first {
ppr = ppr.unwrap().recurse().unwrap().1;
}
while let PacketParserResult::Some(pp) = ppr {
let tag = pp.packet.tag();
for t in keep.iter() {
if *t == tag {
return PacketParserResult::Some(pp);
}
}
let mut ok = false;
for t in skip.iter() {
if *t == tag {
ok = true;
}
}
if !ok {
panic!("Packet not in keep ({:?}) or skip ({:?}) set: {:?}",
keep, skip, pp.packet);
}
ppr = pp.recurse().unwrap().1;
}
return ppr;
}
#[test]
fn decrypt_test() {
for test in DECRYPT_TESTS.iter() { for stream in [false, true].iter() {
eprintln!("Decrypting {}, streaming content: {}",
test.filename, stream);
let path = path_to(test.filename);
let mut ppr = PacketParserBuilder::from_file(&path).unwrap()
.buffer_unread_content()
.finalize()
.expect(&format!("Error reading {}", test.filename)[..]);
let mut ppr = consume_until(
ppr, false, &[ Tag::SEIP, Tag::AED ][..],
&[ Tag::SKESK, Tag::PKESK ][..] );
if let PacketParserResult::Some(ref mut pp) = ppr {
let key = ::conversions::from_hex(test.key_hex, false)
.unwrap().into();
pp.decrypt(test.algo, &key).unwrap();
} else {
panic!("Expected a SEIP/AED packet. Got: {:?}", ppr);
}
let mut ppr = consume_until(
ppr, true, &[ Tag::Literal ][..],
&[ Tag::OnePassSig, Tag::CompressedData ][..]);
if let PacketParserResult::Some(ref mut pp) = ppr {
if *stream {
let mut body = Vec::new();
loop {
let mut b = [0];
if pp.read(&mut b).unwrap() == 0 {
break;
}
body.push(b[0]);
}
assert_eq!(&body[..],
&test.plaintext.content()[..],
"{:?}", pp.packet);
} else {
pp.buffer_unread_content().unwrap();
assert_eq!(&pp.packet.body.as_ref().unwrap()[..],
&test.plaintext.content()[..],
"{:?}", pp.packet);
}
} else {
panic!("Expected a Literal packet. Got: {:?}", ppr);
}
let mut ppr = consume_until(
ppr, true, &[ Tag::MDC ][..], &[ Tag::Signature ][..]);
if let PacketParserResult::Some(
PacketParser { packet: Packet::MDC(ref mdc), .. }) = ppr
{
assert_eq!(mdc.computed_hash(), mdc.hash(),
"MDC doesn't match");
}
if ppr.is_none() {
continue;
}
let ppr = consume_until(
ppr, true, &[][..], &[][..]);
assert!(ppr.is_none());
}}
}
#[test]
fn message_validator() {
for test in DECRYPT_TESTS.iter() {
let path = path_to(test.filename);
let mut ppr = PacketParserBuilder::from_file(&path).unwrap()
.finalize()
.expect(&format!("Error reading {}", test.filename)[..]);
let mut saw_literal = false;
while let PacketParserResult::Some(mut pp) = ppr {
assert!(pp.possible_message());
match pp.packet {
Packet::SEIP(_) | Packet::AED(_) => {
let key = ::conversions::from_hex(test.key_hex, false)
.unwrap().into();
pp.decrypt(test.algo, &key).unwrap();
},
Packet::Literal(_) => {
assert!(! saw_literal);
saw_literal = true;
},
_ => {},
}
ppr = pp.recurse().unwrap().1;
}
assert!(saw_literal);
if let PacketParserResult::EOF(eof) = ppr {
assert!(eof.is_message());
} else {
unreachable!();
}
}
}
#[test]
fn keyring_validator() {
for test in &["keys/testy.pgp",
"keys/lutz.gpg",
"keys/testy-new.pgp",
"keys/neal.pgp"]
{
let path = path_to_data(test);
let mut ppr = PacketParserBuilder::from_reader(
File::open(path_to_data("keys/testy.pgp")).unwrap().chain(
File::open(&path).unwrap())).unwrap()
.finalize()
.expect(&format!("Error reading {:?}", path));
while let PacketParserResult::Some(mut pp) = ppr {
assert!(pp.possible_keyring());
ppr = pp.recurse().unwrap().1;
}
if let PacketParserResult::EOF(eof) = ppr {
assert!(eof.is_keyring());
assert!(! eof.is_tpk());
} else {
unreachable!();
}
}
}
#[test]
fn tpk_validator() {
for test in &["keys/testy.pgp",
"keys/lutz.gpg",
"keys/testy-new.pgp",
"keys/neal.pgp"]
{
let path = path_to_data(test);
let mut ppr = PacketParserBuilder::from_file(&path).unwrap()
.finalize()
.expect(&format!("Error reading {:?}", path));
while let PacketParserResult::Some(mut pp) = ppr {
assert!(pp.possible_keyring());
assert!(pp.possible_tpk());
ppr = pp.recurse().unwrap().1;
}
if let PacketParserResult::EOF(eof) = ppr {
assert!(eof.is_keyring());
assert!(eof.is_tpk());
} else {
unreachable!();
}
}
}
#[test]
fn message_validator_opaque_content() {
for test in DECRYPT_TESTS.iter() {
let path = path_to(test.filename);
let mut ppr = PacketParserBuilder::from_file(&path).unwrap()
.finalize()
.expect(&format!("Error reading {}", test.filename)[..]);
let mut saw_literal = false;
while let PacketParserResult::Some(mut pp) = ppr {
assert!(pp.possible_message());
match pp.packet {
Packet::Literal(_) => {
assert!(! saw_literal);
saw_literal = true;
},
_ => {},
}
ppr = pp.recurse().unwrap().1;
}
assert!(! saw_literal);
if let PacketParserResult::EOF(eof) = ppr {
eprintln!("eof: {:?}", eof);
assert!(eof.is_message());
} else {
unreachable!();
}
}
}
#[test]
fn path() {
for test in DECRYPT_TESTS.iter() {
eprintln!("Decrypting {}", test.filename);
let path = path_to(test.filename);
let mut ppr = PacketParserBuilder::from_file(&path).unwrap()
.finalize()
.expect(&format!("Error reading {}", test.filename)[..]);
let mut last_path = vec![];
let mut paths = test.paths.to_vec();
paths.reverse();
while let PacketParserResult::Some(mut pp) = ppr {
let path = paths.pop().expect("Message longer than expect");
assert_eq!(path.0, pp.packet.tag());
assert_eq!(path.1, pp.path());
assert_eq!(last_path, pp.last_path());
last_path = pp.path.to_vec();
eprintln!(" {}: {:?}", pp.packet.tag(), pp.path());
match pp.packet {
Packet::SEIP(_) | Packet::AED(_) => {
let key = ::conversions::from_hex(test.key_hex, false)
.unwrap().into();
pp.decrypt(test.algo, &key).unwrap();
}
_ => (),
}
ppr = pp.recurse().unwrap().1;
}
paths.reverse();
assert_eq!(paths.len(), 0,
"Message shorter than expected (expecting: {:?})",
paths);
if let PacketParserResult::EOF(mut eof) = ppr {
assert_eq!(last_path, eof.last_path());
} else {
panic!("Expect an EOF");
}
}
}
#[test]
fn corrupted_tpk() {
use armor::{Reader, Kind};
let mut ppr = PacketParser::from_reader(
Reader::from_bytes(bytes!("../keys/corrupted.pgp"),
Some(Kind::PublicKey)))
.unwrap();
let mut sigs = 0;
let mut subkeys = 0;
let mut userids = 0;
let mut uas = 0;
let mut unknown = 0;
while let PacketParserResult::Some(pp) = ppr {
match pp.packet {
Packet::Signature(_) => sigs += 1,
Packet::PublicSubkey(_) => subkeys += 1,
Packet::UserID(_) => userids += 1,
Packet::UserAttribute(_) => uas += 1,
Packet::Unknown(ref u) => {
unknown += 1;
assert_match!(Some(&Error::MalformedPacket(_))
= u.error().downcast_ref());
},
_ => (),
}
ppr = pp.next().unwrap().1;
}
assert_eq!(sigs, 53);
assert_eq!(subkeys, 3);
assert_eq!(userids, 5);
assert_eq!(uas, 1);
assert_eq!(unknown, 1);
}
#[test]
fn junk_prefix() {
let msg = bytes!("../messages/sig.gpg");
let ppr = PacketParserBuilder::from_bytes(msg).unwrap()
.dearmor(packet_parser_builder::Dearmor::Disabled)
.finalize();
assert_match!(Ok(PacketParserResult::Some(ref _pp)) = ppr);
let mut msg2 = Vec::new();
msg2.push(0);
msg2.extend_from_slice(&msg[..]);
let ppr = PacketParserBuilder::from_bytes(&msg2[..]).unwrap()
.dearmor(packet_parser_builder::Dearmor::Disabled)
.finalize();
assert_match!(Err(_) = ppr);
}
}