diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2019-02-20 14:50:51 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2019-02-20 14:50:51 +0100 |
commit | be30d2265146ad00f3be8dcb02112dda9b2742ec (patch) | |
tree | 39a2f8f465e12826a30a2797ccb8ad9a01cc545f /openpgp/src/packet/user_attribute.rs | |
parent | be3a0da872387a13f2d94b313b91425155cb5832 (diff) |
openpgp: Support user attribute subpackets.
- Fixes #191.
Diffstat (limited to 'openpgp/src/packet/user_attribute.rs')
-rw-r--r-- | openpgp/src/packet/user_attribute.rs | 147 |
1 files changed, 146 insertions, 1 deletions
diff --git a/openpgp/src/packet/user_attribute.rs b/openpgp/src/packet/user_attribute.rs index 1a14a052..3df01e3b 100644 --- a/openpgp/src/packet/user_attribute.rs +++ b/openpgp/src/packet/user_attribute.rs @@ -1,7 +1,20 @@ +//! User Attribute packets and subpackets. +//! +//! See [Section 5.12 of RFC 4880] for details. +//! +//! [Section 5.12 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.12 + use std::fmt; use quickcheck::{Arbitrary, Gen}; -use packet; +use buffered_reader::{BufferedReader, BufferedReaderMemory}; + +use Error; +use Result; +use packet::{ + self, + BodyLength, +}; use Packet; /// Holds a UserAttribute packet. @@ -62,6 +75,13 @@ impl UserAttribute { pub fn set_user_attribute(&mut self, value: &[u8]) -> Vec<u8> { ::std::mem::replace(&mut self.value, value.to_vec()) } + + /// Iterates over the subpackets. + pub fn subpackets(&self) -> SubpacketIterator { + SubpacketIterator { + reader: BufferedReaderMemory::new(&self.value[..]), + } + } } impl From<UserAttribute> for Packet { @@ -78,6 +98,100 @@ impl Arbitrary for UserAttribute { } } +/// Iterates over subpackets. +pub struct SubpacketIterator<'a> { + reader: BufferedReaderMemory<'a, ()>, +} + +impl<'a> Iterator for SubpacketIterator<'a> { + type Item = Result<Subpacket>; + fn next(&mut self) -> Option<Self::Item> { + let length = match BodyLength::parse_new_format(&mut self.reader) { + Ok(BodyLength::Full(l)) => l, + Ok(BodyLength::Partial(_)) | Ok(BodyLength::Indeterminate) => + return Some(Err(Error::MalformedPacket( + "Partial or Indeterminate length of subpacket".into()) + .into())), + Err(e) => + if e.kind() == ::std::io::ErrorKind::UnexpectedEof { + return None; + } else { + return Some(Err(e.into())); + }, + }; + + let raw = match self.reader.data_consume_hard(length as usize) { + Ok(r) => &r[..length as usize], + Err(e) => return Some(Err(e.into())), + }; + + if raw.len() == 0 { + return Some(Err(Error::MalformedPacket( + "Subpacket without type octet".into()).into())); + } + + let typ = raw[0]; + let raw = &raw[1..]; + match typ { + // Image. + 1 => if raw.len() >= 16 && + &raw[..3] == &[0x10, 0x00, 0x01] + && raw[4..16].iter().all(|b| *b == 0) + { + let image_kind = raw[3]; + Some(Ok(Subpacket::Image(match image_kind { + 1 => + Image::JPEG(Vec::from(&raw[16..]).into_boxed_slice()), + n @ 100...110 => + Image::Private( + n, Vec::from(&raw[16..]).into_boxed_slice()), + n => + Image::Unknown( + n, Vec::from(&raw[16..]).into_boxed_slice()), + }))) + } else { + Some(Err(Error::MalformedPacket( + "Malformed image subpacket".into()).into())) + }, + n => + Some(Ok(Subpacket::Unknown( + n, Vec::from(raw).into_boxed_slice()))), + } + } +} + +/// User Attribute subpackets. +/// +/// See [Section 5.12 of RFC 4880] for details. +/// +/// [Section 5.12 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.12 +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum Subpacket { + /// Image subpacket. + /// + /// See [Section 5.12.1 of RFC 4880] for details. + /// + /// [Section 5.12.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.12.1 + Image(Image), + /// Unknown subpacket. + Unknown(u8, Box<[u8]>), +} + +/// Image subpacket. +/// +/// See [Section 5.12.1 of RFC 4880] for details. +/// +/// [Section 5.12.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.12.1 +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum Image { + /// A JPEG image format. + JPEG(Box<[u8]>), + /// Private, experimental image format. + Private(u8, Box<[u8]>), + /// Unknown image format. + Unknown(u8, Box<[u8]>), +} + #[cfg(test)] mod tests { use super::*; @@ -91,4 +205,35 @@ mod tests { true } } + + #[test] + fn image() { + let ua = UserAttribute::from_bytes(b" +-----BEGIN PGP ARMORED FILE----- + +0cFuwWwBEAABAQAAAAAAAAAAAAAAAP/Y/+AAEEpGSUYAAQEBASwBLAAA//4AE0Ny +ZWF0ZWQgd2l0aCBHSU1Q/9sAQwADAgIDAgIDAwMDBAMDBAUIBQUEBAUKBwcGCAwK +DAwLCgsLDQ4SEA0OEQ4LCxAWEBETFBUVFQwPFxgWFBgSFBUU/9sAQwEDBAQFBAUJ +BQUJFA0LDRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU +FBQUFBQUFBQU/8IAEQgAAQABAwERAAIRAQMRAf/EABQAAQAAAAAAAAAAAAAAAAAA +AAj/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAFUn//EABQQAQAA +AAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/ +2gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Bf//EABQQ +AQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAAAAAAAAA +AAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQn//EABQRAQAAAAAAAAAAAAAAAAAA +AAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//E +ABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z +=nUQg +-----END PGP ARMORED FILE----- +").unwrap(); + let subpackets: Vec<_> = ua.subpackets().collect(); + assert_eq!(subpackets.len(), 1); + if let Ok(Subpacket::Image(Image::JPEG(img))) = &subpackets[0] { + assert_eq!(img.len(), 539); + assert_eq!(&img[6..10], b"JFIF"); + assert_eq!(&img[24..41], b"Created with GIMP"); + } else { + panic!("Expected JPEG, got {:?}", &subpackets[0]); + } + } } |