summaryrefslogtreecommitdiffstats
path: root/src/openpgp
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@gnu.org>2017-10-16 10:15:57 +0200
committerNeal H. Walfield <neal@gnu.org>2017-10-16 10:15:57 +0200
commit6f28ab1c4a3caae0eaf8650e4611f3ccf3a7f598 (patch)
tree25ca714030e182393dc6dbed527bfc43485e0d54 /src/openpgp
openpgp: Add the start of a parser in nom.
Diffstat (limited to 'src/openpgp')
-rw-r--r--src/openpgp/build/build.rs1
-rw-r--r--src/openpgp/build/mod.rs2
-rw-r--r--src/openpgp/mod.rs8
-rw-r--r--src/openpgp/openpgp.rs288
-rw-r--r--src/openpgp/parse/literal-mode-b.asc1
-rw-r--r--src/openpgp/parse/literal-mode-t-partial-body.asc99
-rw-r--r--src/openpgp/parse/mod.rs2
-rw-r--r--src/openpgp/parse/parse.rs541
-rw-r--r--src/openpgp/parse/public-key.ascbin0 -> 25148 bytes
-rw-r--r--src/openpgp/parse/sig.ascbin0 -> 310 bytes
-rw-r--r--src/openpgp/serialize/mod.rs2
-rw-r--r--src/openpgp/serialize/serialize.rs1
12 files changed, 945 insertions, 0 deletions
diff --git a/src/openpgp/build/build.rs b/src/openpgp/build/build.rs
new file mode 100644
index 00000000..987d76fd
--- /dev/null
+++ b/src/openpgp/build/build.rs
@@ -0,0 +1 @@
+//! Create OpenPGP packets in memory using the builder pattern.
diff --git a/src/openpgp/build/mod.rs b/src/openpgp/build/mod.rs
new file mode 100644
index 00000000..839c55e3
--- /dev/null
+++ b/src/openpgp/build/mod.rs
@@ -0,0 +1,2 @@
+// Hack so that the file doesn't have to be named mod.rs.
+include!("build.rs");
diff --git a/src/openpgp/mod.rs b/src/openpgp/mod.rs
new file mode 100644
index 00000000..d0175ea5
--- /dev/null
+++ b/src/openpgp/mod.rs
@@ -0,0 +1,8 @@
+// Hack so that the file doesn't have to be named mod.rs.
+// Unfortunately, it seems that putting 'pub mod xxx' declarations in
+// an included file confuses rust (it looks for the module in the
+// wrong place). Hence, that here as well.
+
+pub mod parse;
+
+include!("openpgp.rs");
diff --git a/src/openpgp/openpgp.rs b/src/openpgp/openpgp.rs
new file mode 100644
index 00000000..e366c22a
--- /dev/null
+++ b/src/openpgp/openpgp.rs
@@ -0,0 +1,288 @@
+// Machinery for parsing and serializing OpenPGP packet headers.
+
+use std::ops::Deref;
+
+/// The OpenPGP packet types. The values correspond to the serialized
+/// format. The packet types named UnassignedXX are not in use as of
+/// RFC 4880.
+#[derive(Debug)]
+#[derive(FromPrimitive)]
+#[derive(ToPrimitive)]
+// We need PartialEq so that assert_eq! works.
+#[derive(PartialEq)]
+#[derive(Clone, Copy)]
+pub enum Tag {
+ Reserved0 = 0,
+ /* Public-Key Encrypted Session Key Packet. */
+ PKESK = 1,
+ Signature = 2,
+ /* Symmetric-Key Encrypted Session Key Packet. */
+ SKESK = 3,
+ /* One-Pass Signature Packet. */
+ OnePassSig,
+ SecretKey = 5,
+ PublicKey = 6,
+ SecretSubkey = 7,
+ Compressed = 8,
+ /* Symmetrically Encrypted Data Packet. */
+ SED = 9,
+ Marker = 10,
+ Literal = 11,
+ Trust = 12,
+ UserID = 13,
+ PublicSubkey = 14,
+
+ Unassigned15 = 15,
+ Unassigned16 = 16,
+
+ UserAttribute = 17,
+ /* Sym. Encrypted and Integrity Protected Data Packet. */
+ SEIP = 18,
+ /* Modification Detection Code Packet. */
+ MDC = 19,
+
+ /* Unassigned packets (as of RFC4880). */
+ Unassigned20 = 20,
+ Unassigned21 = 21,
+ Unassigned22 = 22,
+ Unassigned23 = 23,
+ Unassigned24 = 24,
+ Unassigned25 = 25,
+ Unassigned26 = 26,
+ Unassigned27 = 27,
+ Unassigned28 = 28,
+ Unassigned29 = 29,
+
+ Unassigned30 = 30,
+ Unassigned31 = 31,
+ Unassigned32 = 32,
+ Unassigned33 = 33,
+ Unassigned34 = 34,
+ Unassigned35 = 35,
+ Unassigned36 = 36,
+ Unassigned37 = 37,
+ Unassigned38 = 38,
+ Unassigned39 = 39,
+
+ Unassigned40 = 40,
+ Unassigned41 = 41,
+ Unassigned42 = 42,
+ Unassigned43 = 43,
+ Unassigned44 = 44,
+ Unassigned45 = 45,
+ Unassigned46 = 46,
+ Unassigned47 = 47,
+ Unassigned48 = 48,
+ Unassigned49 = 49,
+
+ Unassigned50 = 50,
+ Unassigned51 = 51,
+ Unassigned52 = 52,
+ Unassigned53 = 53,
+ Unassigned54 = 54,
+ Unassigned55 = 55,
+ Unassigned56 = 56,
+ Unassigned57 = 57,
+ Unassigned58 = 58,
+ Unassigned59 = 59,
+
+ /* Experimental packets. */
+ Private0 = 60,
+ Private1 = 61,
+ Private2 = 62,
+ Private3 = 63,
+}
+
+/// OpenPGP defines two packet formats: the old and the new format.
+/// They both include the packet's so-called tag.
+#[derive(Debug)]
+pub struct CTBCommon {
+ tag: Tag,
+}
+
+#[derive(Debug)]
+pub struct CTBNew {
+ common: CTBCommon,
+}
+
+// Allow transparent access of common fields.
+impl Deref for CTBNew {
+ type Target = CTBCommon;
+
+ fn deref(&self) -> &Self::Target {
+ &self.common
+ }
+}
+
+#[derive(Debug)]
+#[derive(FromPrimitive)]
+#[derive(Clone, Copy)]
+pub enum PacketLengthType {
+ OneOctet = 0,
+ TwoOctets = 1,
+ FourOctets = 2,
+ Indeterminate = 3,
+}
+
+#[derive(Debug)]
+pub struct CTBOld {
+ common: CTBCommon,
+ length_type: PacketLengthType,
+}
+
+// Allow transparent access of common fields.
+impl Deref for CTBOld {
+ type Target = CTBCommon;
+
+ fn deref(&self) -> &Self::Target {
+ &self.common
+ }
+}
+
+#[derive(Debug)]
+pub enum CTB {
+ New(CTBNew),
+ Old(CTBOld),
+}
+
+// Allow transparent access of common fields.
+impl Deref for CTB {
+ type Target = CTBCommon;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ &CTB::New(ref ctb) => return &ctb.common,
+ &CTB::Old(ref ctb) => return &ctb.common,
+ }
+ }
+}
+
+/// The size of a packet. If Partial(x), then x indicates the number
+/// of bytes remaining in the current chunk. The chunk is followed by
+/// another new format length header, which can be read using
+/// body_length_new_format(). If Indeterminate, then the packet
+/// continues until the end of the input.
+#[derive(Debug)]
+// We need PartialEq so that assert_eq! works.
+#[derive(PartialEq)]
+pub enum BodyLength {
+ Full(u32),
+ /* The size parameter is the size of the initial block. */
+ Partial(u32),
+ Indeterminate,
+}
+
+#[derive(Debug)]
+pub struct PacketCommon {
+ tag: Tag,
+}
+
+/// An OpenPGP packet's header.
+#[derive(Debug)]
+pub struct Header {
+ ctb: CTB,
+ length: BodyLength,
+}
+
+#[derive(Debug)]
+pub struct Signature<'a> {
+ common: PacketCommon,
+ version: u8,
+ sigtype: u8,
+ pk_algo: u8,
+ hash_algo: u8,
+ hashed_area: &'a[u8],
+ unhashed_area: &'a[u8],
+ hash_prefix: [u8; 2],
+ mpis: &'a[u8],
+}
+
+// Allow transparent access of common fields.
+impl<'a> Deref for Signature<'a> {
+ type Target = PacketCommon;
+
+ fn deref(&self) -> &Self::Target {
+ &self.common
+ }
+}
+
+#[derive(Debug)]
+pub struct Key<'a> {
+ common: PacketCommon,
+ version: u8,
+ /* When the key was created. */
+ creation_time: u32,
+ pk_algo: u8,
+ mpis: &'a [u8],
+}
+
+// Allow transparent access of common fields.
+impl<'a> Deref for Key<'a> {
+ type Target = PacketCommon;
+
+ fn deref(&self) -> &Self::Target {
+ &self.common
+ }
+}
+
+#[derive(Debug)]
+pub struct UserID<'a> {
+ common: PacketCommon,
+ value: &'a [u8],
+}
+
+// Allow transparent access of common fields.
+impl<'a> Deref for UserID<'a> {
+ type Target = PacketCommon;
+
+ fn deref(&self) -> &Self::Target {
+ &self.common
+ }
+}
+
+#[derive(Debug)]
+pub struct Literal<'a> {
+ common: PacketCommon,
+ format: u8,
+ /* filename is a string, but strings in Rust are valid UTF-8.
+ * But, there is no guarantee that the filename is valid UTF-8.
+ * Thus, we leave filename as a byte array. It can be converted
+ * to a string using String::from_utf8() or
+ * String::from_utf8_lossy(). */
+ filename: &'a [u8],
+ date: u32,
+ content: &'a [u8],
+}
+
+// Allow transparent access of common fields.
+impl<'a> Deref for Literal<'a> {
+ type Target = PacketCommon;
+
+ fn deref(&self) -> &Self::Target {
+ &self.common
+ }
+}
+
+#[derive(Debug)]
+pub enum Packet<'a> {
+ Signature(Signature<'a>),
+ PublicKey(Key<'a>),
+ PublicSubkey(Key<'a>),
+ SecretKey(Key<'a>),
+ SecretSubkey(Key<'a>),
+ UserID(UserID<'a>),
+ Literal(Literal<'a>),
+}
+
+// Allow transparent access of common fields.
+impl<'a> Deref for Packet<'a> {
+ type Target = PacketCommon;
+
+ fn deref(&self) -> &Self::Target {
+ match self {
+ &Packet::Signature(ref packet) => &packet.common,
+ &Packet::Literal(ref packet) => &packet.common,
+ _ => unimplemented!(),
+ }
+ }
+}
diff --git a/src/openpgp/parse/literal-mode-b.asc b/src/openpgp/parse/literal-mode-b.asc
new file mode 100644
index 00000000..61609bfd
--- /dev/null
+++ b/src/openpgp/parse/literal-mode-b.asc
@@ -0,0 +1 @@
+ËbfoobarYÙþ¸FOOBAR \ No newline at end of file
diff --git a/src/openpgp/parse/literal-mode-t-partial-body.asc b/src/openpgp/parse/literal-mode-t-partial-body.asc
new file mode 100644
index 00000000..8e43cdec
--- /dev/null
+++ b/src/openpgp/parse/literal-mode-t-partial-body.asc
@@ -0,0 +1,99 @@
+Ëìt manifesto.txtYâC‰A Cypherpunk's Manifesto
+by Eric Hughes
+
+Privacy is necessary for an open society in the electronic
+age. Privacy is not secrecy. A private matter is something one doesn't
+want the whole world to know, but a secret matter is something one
+doesn't want anybody to know. Privacy is the power to selectively
+reveal oneself to the world.
+
+If two parties have some sort of dealings, then each has a memory of
+their interaction. Each party can speak about their own memory of
+this; how could anyone prevent it? One could pass laws against it, but
+the freedom of speech, even more than privacy, is fundamental to an
+open society; we seek not to restrict any speech at all. If many
+parties speak together in the same forum, each can speak to all the
+others and aggregate together knowledge about individuals and other
+parties. The power of electronic communications has enabled such group
+speech, and it will not go away merely because we might want it to.
+
+Since we desire privacy, we must ensure that each party to a
+transaction have knowledge only of that which is directly necessary
+for that transaction. Since any information can be spoken of, we must
+ensure that we reveal as little as possible. In most cases personal
+identity is not salient. When I purchase a magazine at a store and
+hand cash to the clerk, there is no need to know who I am. When I ask
+my electronic mail provider to send and receive messages, my provider
+need not know to whom I am speaking or what I am saying or what others
+are saying to me; my provider only need know how to get the message
+there and how much I owe them in fees. When my identity is revealed by
+the underlying mechanism of the transaction, I have no privacy. I
+cannot here selectively reveal myself; I must always reveal myself.
+
+Therefore, privacy in an open society requires anonymous transaction
+systems. Until now, cash has been the primary such system. An
+anonymous transaction system is not a secret transaction system. An
+anonymous system empowers individuals to reveal their identity when
+desired and only when desired; this is the essence of privacy.
+
+Privacy in an open society also requires cryptography. If I say
+something, I want it heard only by those for whom I intend it. If the
+content of my speech is available to the world, I have no privacy. To
+encrypt is to indicate the desire for privacy, and to encrypt with
+weak cryptography is to indicate not too much desire for
+privacy. Furthermore, to reveal one's identity with assurance when the
+default is anonymity requires the cryptographic signature.
+
+We cannot expect governments, corporations, or other large, faceless
+organizations to grant us privacy out of their beneficence. It is to
+their advantage to speak of us, and we should expect that they will
+speak. To try to prevent their speech is to fight against the
+realities of information. Information does not just want to be free,
+it longs to be free. Information expands to fill the available storage
+space. Information is Rumor's younger, stronger cousin; Information is
+fleeter of foot, has more eyes, knows more, and understands less than
+Rumor.
+
+We must defend our own privacy if we expect to have any. We must come
+together and create systems which allow anonymous transactions to take
+place. People have been defending their own privacy for centuries with
+whispers, darkness, envelopes, closed doors, secret handshakes, and
+couriers. The technologies of the past did not allow for strong
+privacy, but electronic technologies do.
+
+We the Cypherpunks are dedicated to building anonymous systems. We are
+defending our privacy with cryptography, with anonymous mail
+forwarding systems, with digital signatures, and with electronic
+money.
+
+Cypherpunks write code. We know that someone has to write software to
+defend privacy, and since we can't get privacy unless we all do, we're
+going to write it. We publish our code so that our fellow Cypherpunks
+may practice and play with it. Our code is free for all to use,
+worldwide. We don't much care if you don't approve of the software we
+write. We know that softwaêre can't be destroyed and that a widely
+dispersed system can't be shut down.
+
+Cypherpunks deplore regulations on cryptography, for encryption is
+fundamentally a private act. The act of encryption, in fact, removes
+information from the public realm. Even laws against cryptography
+reach only so far as a nation's border and the arm of its
+violence. Cryptography will ineluctably spread over the whole globe,
+and with it the anonymous transactions systems that it makes possible.
+
+For privacy to be widespread it must be part of a social
+contract. People must come and together deploy these systems for the
+common good. Privacy only extends so far as the cooperation of one's
+fellows in society. We the Cypherpunks seek your questions and your
+concerns and hope we may engage you so that we do not deceive
+ourselves. We will not, however, be moved out of our course because
+some may disagree with our goals.
+
+The Cypherpunks are actively engaged in making the networks safer for
+privacy. Let us proceed together apace.
+
+Onward9.
+
+Eric Hughes <hughes@soda.berkeley.edu>
+
+9 March 1993
diff --git a/src/openpgp/parse/mod.rs b/src/openpgp/parse/mod.rs
new file mode 100644
index 00000000..d684d0e5
--- /dev/null
+++ b/src/openpgp/parse/mod.rs
@@ -0,0 +1,2 @@
+// Hack so that the file doesn't have to be named mod.rs.
+include!("parse.rs");
diff --git a/src/openpgp/parse/parse.rs b/src/openpgp/parse/parse.rs
new file mode 100644
index 00000000..930c6c1f
--- /dev/null
+++ b/src/openpgp/parse/parse.rs
@@ -0,0 +1,541 @@
+// http://rust.unhandledexpression.com/nom/enum.IResult.html
+// I = Input, O = Output, E = Error
+//
+// pub enum IResult<I, O, E = u32> {
+// // Correct parsing. I = rest of unparsed data; O = the parser's result
+// Done(I, O),
+// // An error.
+// Error(Err<E>),
+// //
+// Incomplete(Needed),
+// }
+//
+// The named! macro is shorthand for creating an appropriate type
+// signature for a nom combinator. Normally, it is used like this:
+//
+// named!(my_parser, parser_body)
+//
+// If the function name includes a generic type, then that can be used
+// to override the type of the output in the IResult. For instance:
+//
+// named!(word<&str>, map_res!(take_while!(nom::is_alphabetic), str::from_utf8));
+//
+// preceded! takes two parsers. The first is a prefix to match. If
+// the prefix matches, then it is discarded and the suffix is matched
+// and returned. This is useful when matching a signature or
+// delimiter that isn't needed for further processing.
+//
+// preceded!(prefix, suffix) -> suffix
+
+// delimited! is like preceded, but it takes three parsers and returns
+// what the result of the middle parser if the first and last parser
+// succeed. It is useful for extracting name from: "[ name ]"
+
+// take_while! is a simple parser that accumulates data from the input
+// stream as long as the provided callback returns true.
+//
+// fn is_digit(c: u8) -> bool { c >= '0' as u8 && c <= '9' as u8 }
+// named!(number, take_while!(is_digit));
+// let r = number(&b"1234after"[..]);
+// assert_eq!(r, IResult::Done(&b"after"[..], &b"1234"[..]));
+//
+// The map_res macro applies a function to the result portion of an
+// IResult. This is useful, for instance, to convert a byte array to
+// a string:
+//
+// named!(word<&str>, map_res!(take_while!(nom::is_alphabetic), str::from_utf8));
+// let r = word(&b"hello, world"[..]);
+// assert_eq!(r, IResult::Done(&b", world"[..], &"hello"[..]));
+
+// use nom::HexDisplay;
+
+use num;
+
+use nom;
+use nom::{IResult,be_u16,be_u32};
+
+use super::*;
+
+macro_rules! try_iresult (
+ ($i:expr) => (
+ match $i {
+ nom::IResult::Done(i,o) => (i,o),
+ nom::IResult::Error(e) => return nom::IResult::Error(e),
+ nom::IResult::Incomplete(i) => return nom::IResult::Incomplete(i)
+ }
+ );
+);
+
+// Packet headers.
+
+// Parse a CTB (as described in Section 4.2 of RFC4880) and return a
+// 'struct CTB'. This function parses both new and old format ctbs.
+//
+// Example:
+//
+// let (input, ctb) = ctb(data).unwrap();
+// println!("header: {:?}", ctb);
+// assert_eq!(ctb.tag, 11);
+named!(
+ ctb<CTB>,
+ bits!(
+ do_parse!(
+ tag_bits!(u8, 1, 1) >>
+ r: switch!(take_bits!(u8, 1),
+ /* New format. */
+ 1 => do_parse!(tag: take_bits!(u8, 6) >>
+ (CTB::New(CTBNew {
+ common: CTBCommon {
+ tag: num::FromPrimitive::from_u8(tag).unwrap()
+ },
+ }))) |
+ /* Old format packet. */
+ 0 => do_parse!(tag: take_bits!(u8, 4) >>
+ length_type: take_bits!(u8, 2) >>
+ (CTB::Old(CTBOld {
+ common: CTBCommon {
+ tag: num::FromPrimitive::from_u8(tag).unwrap()
+ },
+ length_type:
+ num::FromPrimitive::from_u8(length_type).unwrap(),
+ })))
+ ) >>
+ (r))));
+
+/// Decode a new format body length (as described in Section 4.2.2 of RFC4880).
+///
+/// Example:
+///
+/// assert_eq!(body_length_new_format(&[0x64][..]),
+/// nom::IResult::Done(&b""[..], BodyLength::Full(100)));
+pub fn body_length_new_format(input: &[u8]) -> IResult<&[u8], BodyLength> {
+ fn to_u8 (x: &[u8]) -> u8 {
+ assert_eq!(x.len(), 1);
+ x[0]
+ }
+
+ match map!(input, take!(1), to_u8) {
+ IResult::Done(input, octet1) if octet1 < 192 => {
+ /* One octet. */
+ return IResult::Done(input, BodyLength::Full(octet1 as u32));
+ },
+ IResult::Done(input, octet1) if 192 <= octet1 && octet1 < 224 => {
+ /* Two octets length. */
+ match map!(input, take!(1), to_u8) {
+ IResult::Done(input, octet2) => {
+ return IResult::Done(
+ input,
+ BodyLength::Full(((octet1 as u32 - 192) << 8) + octet2 as u32 + 192));
+ },
+ IResult::Error(e) => {
+ return IResult::Error(e);
+ },
+ IResult::Incomplete(needed) => {
+ return IResult::Incomplete(needed);
+ },
+ }
+ },
+ IResult::Done(input, octet1) if 224 <= octet1 && octet1 < 255 => {
+ /* Partial body length. */
+ return IResult::Done(input, BodyLength::Partial(1 << (octet1 & 0x1F)));
+ },
+ IResult::Done(input, octet1) if octet1 == 255 => {
+ /* Four octets. */
+ return map!(input, nom::be_u32, |x| BodyLength::Full(x));
+ },
+ // The above is actually exhaustive---it covers all values
+ // that a u8 could assume (or should), but rust doesn't figure
+ // that out, so we add this arm.
+ IResult::Done(_, _) => unreachable!(),
+ IResult::Error(e) => {
+ return IResult::Error(e);
+ },
+ IResult::Incomplete(needed) => {
+ return IResult::Incomplete(needed);
+ },
+ }
+}
+
+#[test]
+fn body_length_new_format_test() {
+ /* Examples from Section 4.2.3 of RFC4880. */
+
+ // Example #1.
+ assert_eq!(body_length_new_format(&[0x64][..]),
+ IResult::Done(&b""[..], BodyLength::Full(100)));
+
+ // Example #2.
+ assert_eq!(body_length_new_format(&[0xC5, 0xFB][..]),
+ IResult::Done(&b""[..], BodyLength::Full(1723)));
+
+ // Example #3.
+ assert_eq!(body_length_new_format(&[0xFF, 0x00, 0x01, 0x86, 0xA0][..]),
+ IResult::Done(&b""[..], BodyLength::Full(100000)));
+
+ // Example #4.
+ assert_eq!(body_length_new_format(&[0xEF][..]),
+ IResult::Done(&b""[..], BodyLength::Partial(32768)));
+ assert_eq!(body_length_new_format(&[0xE1][..]),
+ IResult::Done(&b""[..], BodyLength::Partial(2)));
+ assert_eq!(body_length_new_format(&[0xF0][..]),
+ IResult::Done(&b""[..], BodyLength::Partial(65536)));
+ assert_eq!(body_length_new_format(&[0xC5, 0xDD][..]),
+ IResult::Done(&b""[..], BodyLength::Full(1693)));
+}
+
+fn body_length_old_format<'a>(input: &'a [u8], length_type: PacketLengthType)
+ -> IResult<&'a [u8], BodyLength> {
+ match length_type {
+ PacketLengthType::OneOctet =>
+ return map!(input, nom::be_u8, |x| BodyLength::Full(x as u32)),
+ PacketLengthType::TwoOctets =>
+ return map!(input, nom::be_u16, |x| BodyLength::Full(x as u32)),
+ PacketLengthType::FourOctets =>
+ return map!(input, nom::be_u32, |x| BodyLength::Full(x)),
+ PacketLengthType::Indeterminate =>
+ return IResult::Done(input, BodyLength::Indeterminate),
+ }
+}
+
+#[test]
+fn body_length_old_format_test() {
+ assert_eq!(body_length_old_format(&[1], PacketLengthType::OneOctet),
+ IResult::Done(&b""[..], BodyLength::Full(1)));
+ assert_eq!(body_length_old_format(&[1, 2], PacketLengthType::TwoOctets),
+ IResult::Done(&b""[..], BodyLength::Full((1 << 8) + 2)));
+ assert_eq!(body_length_old_format(&[1, 2, 3, 4], PacketLengthType::FourOctets),
+ IResult::Done(&b""[..],
+ BodyLength::Full((1 << 24) + (2 << 16) + (3 << 8) + 4)));
+ assert_eq!(body_length_old_format(&[1, 2, 3, 4, 5, 6], PacketLengthType::FourOctets),
+ IResult::Done(&[5, 6][..],
+ BodyLength::Full((1 << 24) + (2 << 16) + (3 << 8) + 4)));
+ assert_eq!(body_length_old_format(&[1, 2, 3, 4], PacketLengthType::Indeterminate),
+ IResult::Done(&[1, 2, 3, 4][..], BodyLength::Indeterminate));
+}
+
+/// INPUT is a byte array that presumably contains an OpenPGP packet.
+/// This function parses the packet's header and returns a
+/// deserialized version and the rest input.
+pub fn header(input: &[u8]) -> IResult<&[u8], Header> {
+ let (input, ctb) = try_iresult!(ctb(input));
+ let (input, length) = match ctb {
+ CTB::New(_) => try_iresult!(body_length_new_format(input)),
+ CTB::Old(ref ctb) => try_iresult!(body_length_old_format(input, ctb.length_type)),
+ };
+ return IResult::Done(input, Header { ctb: ctb, length: length });
+}
+
+// Packets.
+
+/// Parse the body of a signature packet.
+pub fn signature_body(header: Header, input: &[u8]) -> IResult<&[u8], Signature> {
+ let len = match header.length {
+ BodyLength::Full(x) => x as usize,
+ BodyLength::Partial(_) => unimplemented!(),
+ BodyLength::Indeterminate => input.len(),
+ };
+
+ if len > input.len() {
+ // XXX: Should we return len or len - input.len()?
+ return IResult::Incomplete(nom::Needed::Size(len));
+ }
+
+ /* Make sure we don't read beyond the end of the packet. */
+ let rest = &input[len..];
+ let input = &input[0..len];
+ let r = do_parse!(
+ input,
+ version: take!(1) >>
+ sigtype: take!(1) >>
+ pk_algo: take!(1) >>
+ hash_algo: take!(1) >>
+ hashed_area_len: be_u16 >>
+ hashed_area: take!(hashed_area_len) >>
+ unhashed_area_len: be_u16 >>
+ unhashed_area: take!(unhashed_area_len) >>
+ hash_prefix: take!(2) >>
+ (Signature {
+ common: PacketCommon {
+ tag: Tag::Signature,
+ },
+ version: version[0],
+ sigtype: sigtype[0],
+ pk_algo: pk_algo[0],
+ hash_algo: hash_algo[0],
+ hashed_area: hashed_area,
+ unhashed_area: unhashed_area,
+ hash_prefix: [hash_prefix[0], hash_prefix[1]],
+ mpis: &b""[..],
+ }));
+
+ if let IResult::Done(content, signature) = r {
+ return IResult::Done(rest, Signature { mpis: content, .. signature });
+