use std::fmt;
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
use buffered_reader::BufferedReader;
use crate::Error;
use crate::Result;
use crate::packet::{
self,
header::BodyLength,
};
use crate::Packet;
use crate::policy::HashAlgoSecurity;
use crate::serialize::Marshal;
use crate::serialize::MarshalInto;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UserAttribute {
pub(crate) common: packet::Common,
value: Vec<u8>,
}
assert_send_and_sync!(UserAttribute);
impl From<Vec<u8>> for UserAttribute {
fn from(u: Vec<u8>) -> Self {
UserAttribute {
common: Default::default(),
value: u,
}
}
}
impl fmt::Debug for UserAttribute {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("UserAttribute")
.field("value (bytes)", &self.value.len())
.finish()
}
}
impl UserAttribute {
pub fn new(subpackets: &[Subpacket]) -> Result<Self> {
let mut value = Vec::with_capacity(
subpackets.iter().fold(0, |l, s| l + s.serialized_len()));
for s in subpackets {
s.serialize(&mut value)?
}
Ok(UserAttribute {
common: Default::default(),
value
})
}
pub fn hash_algo_security(&self) -> HashAlgoSecurity {
HashAlgoSecurity::CollisionResistance
}
pub fn value(&self) -> &[u8] {
self.value.as_slice()
}
pub fn value_mut(&mut self) -> &mut Vec<u8> {
&mut self.value
}
pub fn subpackets(&self) -> SubpacketIterator {
SubpacketIterator {
reader: buffered_reader::Memory::new(&self.value[..]),
}
}
}
impl From<UserAttribute> for Packet {
fn from(s: UserAttribute) -> Self {
Packet::UserAttribute(s)
}
}
#[cfg(test)]
impl Arbitrary for UserAttribute {
fn arbitrary(g: &mut Gen) -> Self {
use crate::arbitrary_helper::gen_arbitrary_from_range;
UserAttribute::new(
&(0..gen_arbitrary_from_range(1..10, g))
.map(|_| Subpacket::arbitrary(g))
.collect::<Vec<_>>()[..]).unwrap()
}
}
pub struct SubpacketIterator<'a> {
reader: buffered_reader::Memory<'a, ()>,
}
assert_send_and_sync!(SubpacketIterator<'_>);
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.is_empty() {
return Some(Err(Error::MalformedPacket(
"Subpacket without type octet".into()).into()));
}
let typ = raw[0];
let raw = &raw[1..];
match typ {
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()))),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Subpacket {
Image(Image),
Unknown(u8, Box<[u8]>),
}
assert_send_and_sync!(Subpacket);
#[cfg(test)]
impl Arbitrary for Subpacket {
fn arbitrary(g: &mut Gen) -> Self {
use crate::arbitrary_helper::gen_arbitrary_from_range;
match gen_arbitrary_from_range(0..3, g) {
0 => Subpacket::Image(Image::arbitrary(g)),
1 => Subpacket::Unknown(
0,
Vec::<u8>::arbitrary(g).into_boxed_slice()
),
2 => Subpacket::Unknown(
gen_arbitrary_from_range(2..256, g) as u8,
Vec::<u8>::arbitrary(g).into_boxed_slice()
),
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Image {
JPEG(Box<[u8]>),
Private(u8, Box<[u8]>),
Unknown(u8, Box<[u8]>),
}
assert_send_and_sync!(Image);
#[cfg(test)]
impl Arbitrary for Image {
fn arbitrary(g: &mut Gen) -> Self {
use crate::arbitrary_helper::gen_arbitrary_from_range;
match gen_arbitrary_from_range(0..5, g) {
0 =>
Image::JPEG(
Vec::<u8>::arbitrary(g).into_boxed_slice()
),
1 =>
Image::Unknown(
gen_arbitrary_from_range(2..100, g),
Vec::<u8>::arbitrary(g).into_boxed_slice()
),
2 =>
Image::Private(
gen_arbitrary_from_range(100..111, g),
Vec::<u8>::arbitrary(g).into_boxed_slice()
),
3 =>
Image::Unknown(
0,
Vec::<u8>::arbitrary(g).into_boxed_slice()
),
4 =>
Image::Unknown(
gen_arbitrary_from_range(111..256, g) as u8,
Vec::<u8>::arbitrary(g).into_boxed_slice()
),
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parse::Parse;
quickcheck! {
fn roundtrip(p: UserAttribute) -> bool {
let buf = p.to_vec().unwrap();
assert_eq!(p.serialized_len(), buf.len());
let q = UserAttribute::from_bytes(&buf).unwrap();
assert_eq!(p, q);
true
}
}
quickcheck! {
fn roundtrip_subpacket(sp: Subpacket) -> bool {
let value = sp.to_vec().unwrap();
assert_eq!(sp.serialized_len(), value.len());
let ua = UserAttribute {
common: Default::default(),
value,
};
let buf = ua.to_vec().unwrap();
let q = UserAttribute::from_bytes(&buf).unwrap();
let subpackets = q.subpackets().collect::<Vec<_>>();
assert_eq!(subpackets.len(), 1);
assert_eq!(&sp, subpackets[0].as_ref().unwrap());
true
}
}
quickcheck! {
fn roundtrip_image(img: Image) -> bool {
let mut body = img.to_vec().unwrap();
assert_eq!(img.serialized_len(), body.len());
let mut value =
BodyLength::Full(1 + body.len() as u32).to_vec().unwrap();
value.push(1); value.append(&mut body);
let ua = UserAttribute {
common: Default::default(),
value,
};
let buf = ua.to_vec().unwrap();
let q = UserAttribute::from_bytes(&buf).unwrap();
let subpackets = q.subpackets().collect::<Vec<_>>();
assert_eq!(subpackets.len(), 1);
if let Ok(Subpacket::Image(i)) = &subpackets[0] {
assert_eq!(&img, i);
} else {
panic!("expected image subpacket, got {:?}", subpackets[0]);
}
true
}
}
#[test]
fn image() {
use crate::Packet;
let p = Packet::from_bytes("
-----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<_> = if let Packet::UserAttribute(ua) = p {
ua.subpackets().collect()
} else {
panic!("Expected an UserAttribute, got: {:?}", p);
};
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]);
}
if let Ok(Subpacket::Image(img)) = &subpackets[0] {
let buf = img.to_vec().unwrap();
assert_eq!(buf.len(), 539 + 16 );
assert_eq!(img.serialized_len(), 539 + 16 );
} else {
unreachable!("decomposed fine before");
}
if let Ok(img) = &subpackets[0] {
let buf = img.to_vec().unwrap();
assert_eq!(buf.len(), 539 + 16 + 3 );
assert_eq!(img.serialized_len(), 539 + 16 + 3 );
} else {
unreachable!("decomposed fine before");
}
}
}