diff options
-rw-r--r-- | src/buffered_reader/memory.rs | 6 | ||||
-rw-r--r-- | src/openpgp/openpgp.rs | 9 | ||||
-rw-r--r-- | src/openpgp/parse/parse.rs | 1 | ||||
-rw-r--r-- | src/openpgp/subpackets.rs | 229 |
4 files changed, 245 insertions, 0 deletions
diff --git a/src/buffered_reader/memory.rs b/src/buffered_reader/memory.rs index aaf54264..327e4bcb 100644 --- a/src/buffered_reader/memory.rs +++ b/src/buffered_reader/memory.rs @@ -29,6 +29,12 @@ impl<'a> BufferedReaderMemory<'a> { cursor: 0, } } + + /// Returns the number of bytes that have been consumed by this + /// reader. + pub fn total_out(&self) -> usize { + return self.cursor; + } } impl<'a> io::Read for BufferedReaderMemory<'a> { diff --git a/src/openpgp/openpgp.rs b/src/openpgp/openpgp.rs index d0d7f023..547f2239 100644 --- a/src/openpgp/openpgp.rs +++ b/src/openpgp/openpgp.rs @@ -3,6 +3,11 @@ use std; use std::ops::{Deref,DerefMut}; +use std::cell::RefCell; +use std::collections::HashMap; + +mod subpackets; + /// The OpenPGP packet types. The values correspond to the serialized /// format. The packet types named UnassignedXX are not in use as of /// RFC 4880. @@ -212,6 +217,10 @@ pub struct Signature { pk_algo: u8, hash_algo: u8, hashed_area: Vec<u8>, + // We parse the subpackets on demand. Since self-referential + // structs are a no-no, we use (start, len) to reference the + // content in hashed_area. + hashed_area_parsed: RefCell<Option<HashMap<u8, (bool, u16, u16)>>>, unhashed_area: Vec<u8>, hash_prefix: [u8; 2], mpis: Vec<u8>, diff --git a/src/openpgp/parse/parse.rs b/src/openpgp/parse/parse.rs index ca131017..a0d72295 100644 --- a/src/openpgp/parse/parse.rs +++ b/src/openpgp/parse/parse.rs @@ -206,6 +206,7 @@ fn signature_parser<'a, R: BufferedReader + 'a>(mut bio: R) pk_algo: pk_algo, hash_algo: hash_algo, hashed_area: hashed_area, + hashed_area_parsed: RefCell::new(None), unhashed_area: unhashed_area, hash_prefix: [hash_prefix1, hash_prefix2], mpis: mpis, diff --git a/src/openpgp/subpackets.rs b/src/openpgp/subpackets.rs new file mode 100644 index 00000000..1f50a511 --- /dev/null +++ b/src/openpgp/subpackets.rs @@ -0,0 +1,229 @@ +use std::io::Error; + +use super::*; +use ::buffered_reader::*; + +#[derive(Debug)] +#[derive(FromPrimitive)] +#[derive(ToPrimitive)] +#[derive(PartialEq)] +#[derive(Clone, Copy)] +pub enum SubpacketTag { + Reserved0 = 0, + Reserved1 = 1, + SignatureCreationTime = 2, + SignatureExpirationTime = 3, + ExportableCertification = 4, + TrustSignature = 5, + RegularExpression = 6, + Revocable = 7, + Reserved = 8, + KeyExpirationTime = 9, + PlaceholderForBackwardCompatibility = 10, + PreferredSymmetricAlgorithms = 11, + RevocationKey = 12, + Reserved13 = 13, + Reserved14 = 14, + Reserved15 = 15, + Issuer = 16, + Reserved17 = 17, + Reserved18 = 18, + Reserved19 = 19, + NotationData = 20, + PreferredHashAlgorithms = 21, + PreferredCompressionAlgorithms = 22, + KeyServerPreferences = 23, + PreferredKeyServer = 24, + PrimaryUserID = 25, + PolicyURI = 26, + KeyFlags = 27, + SignersUserID = 28, + ReasonForRevocation = 29, + Features = 30, + SignatureTarget = 31, + EmbeddedSignature = 32, + // Added in RFC 4880bis. + IssuerFingerprint = 33, + Private100 = 100, + Private101 = 101, + Private102 = 102, + Private103 = 103, + Private104 = 104, + Private105 = 105, + Private106 = 106, + Private107 = 107, + Private108 = 108, + Private109 = 109, + Private110 = 110, +} + +#[derive(Debug,Clone)] +pub struct Subpacket<'a> { + critical: bool, + tag: SubpacketTag, + value: &'a str, +} + +/// Decode a subpacket length as described in Section 5.2.3.1 of RFC 4880. +fn subpacket_length(bio: &mut BufferedReaderMemory) + -> Result<u32, Error> { + let octet1 = bio.data_consume_hard(1)?[0]; + if octet1 < 192 { + // One octet. + return Ok(octet1 as u32); + } + if 192 <= octet1 && octet1 < 255 { + // Two octets length. + let octet2 = bio.data_consume_hard(1)?[0]; + return Ok(((octet1 as u32 - 192) << 8) + octet2 as u32 + 192); + } + + // Five octets. + assert_eq!(octet1, 255); + return Ok(bio.read_be_u32()?); +} + +impl Signature { + fn subpackets_init(&self) -> Result<(), Error> { + if self.hashed_area_parsed.borrow().is_some() { + return Ok(()); + } + + let mut bio = BufferedReaderMemory::new(&self.hashed_area.as_slice()); + + let mut hash = HashMap::new(); + + while bio.data(1)?.len() > 0 { + let len = subpacket_length(&mut bio)?; + + if bio.total_out() + len as usize > self.hashed_area.len() { + // Subpacket extends beyond the end of the hashed + // area. Skip it. + eprintln!("Invalid subpacket: subpacket extends beyond \ + end of hashed area ([{}..{}); {}).", + bio.total_out(), len, self.hashed_area.len()); + break; + } + + if len == 0 { + // Hmm, a zero length packet. In that case, there is + // no header. + continue; + } + + let tag : u8 = bio.data_consume_hard(1)?[0]; + let len = len - 1; + + // The critical bit is the high bit. Extract it. + let critical = tag & (1 << 7) != 0; + // Then clear it from the type. + let tag = tag & !(1 << 7); + + let start = bio.total_out(); + assert!(start <= std::u16::MAX as usize); + assert!(len <= std::u16::MAX as u32); + + hash.insert(tag, (critical, bio.total_out() as u16, len as u16)); + + bio.consume(len as usize); + } + + *self.hashed_area_parsed.borrow_mut() = Some(hash); + + return Ok(()); + } + + pub fn subpacket<'a>(&'a self, tag: u8) -> Option<(bool, &'a [u8])> { + let _ = self.subpackets_init(); + + match self.hashed_area_parsed.borrow().as_ref().unwrap().get(&tag) { + Some(&(critical, start, len)) => + Some((critical, + &self.hashed_area[start as usize + ..start as usize + len as usize])), + None => None, + } + } + + pub fn signature_create_time(&self) { + let _value = self.subpacket(SubpacketTag::SignatureCreationTime as u8); + unimplemented!(); + } + + pub fn signature_expiration_time(&self) { + let _value = self.subpacket(SubpacketTag::SignatureExpirationTime as u8); + unimplemented!(); + } + + // ExportableCertification + // TrustSignature + // RegularExpression + // Revocable + // KeyExpirationTime + // PreferredSymmetricAlgorithms + // RevocationKey + // Issuer + // NotationData + // PreferredHashAlgorithms + // PreferredCompressionAlgorithms + // KeyServerPreferences + // PreferredKeyServer + // PrimaryUserID + // PolicyURI + // KeyFlags + // SignersUserID + // ReasonForRevocation + // Features + // SignatureTarget + // EmbeddedSignature + + pub fn issuer_fingerprint<'a>(&'a self) -> Option<(bool, &'a[u8])> { + self.subpacket(SubpacketTag::IssuerFingerprint as u8) + } +} + +#[test] +fn subpacket_test_1 () { + use std::path::PathBuf; + use std::fs::File; + + let path : PathBuf = [env!("CARGO_MANIFEST_DIR"), + "src", "openpgp", "parse", + "signed.gpg"] + .iter().collect(); + let mut f = File::open(&path).expect(&path.to_string_lossy()); + let bio = BufferedReaderGeneric::new(&mut f, None); + let message = Message::deserialize(bio, None).unwrap(); + eprintln!("Message has {} top-level packets.", message.packets.len()); + eprintln!("Message: {:?}", message); + + let mut count = 0; + for (i, p) in message.iter().enumerate() { + count += 1; + eprintln!("{}: {:?}", i, p); + if let &Packet::Signature(ref sig) = p { + let mut got2 = false; + let mut got33 = false; + + for i in 0..256 { + if let Some((critical, value)) = sig.subpacket(i as u8) { + eprintln!(" {}: {:?}", i, value); + + if i == 2 { + got2 = true; + assert!(!critical); + } else if i == 33 { + got33 = true; + assert!(!critical); + } else { + panic!("Unexpectedly found subpacket {}", i); + } + } + } + + assert!(got2 && got33); + } + } + // We expect 6 packets. + assert_eq!(count, 6); +} |