summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2017-12-12 22:07:25 +0100
committerNeal H. Walfield <neal@pep.foundation>2017-12-12 22:16:16 +0100
commit557eabdb50d1ca6e8c1bf2b644eec504317ab312 (patch)
tree6153b616f66af2df4e1ec23f1dea49fae175936d /src
parent5b0d38acf656edb2b9f35e64ccc82ad555c67d30 (diff)
Add initial support for signature subpackets.
Diffstat (limited to 'src')
-rw-r--r--src/buffered_reader/memory.rs6
-rw-r--r--src/openpgp/openpgp.rs9
-rw-r--r--src/openpgp/parse/parse.rs1
-rw-r--r--src/openpgp/subpackets.rs229
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);
+}