diff options
author | Neal H. Walfield <neal@gnu.org> | 2017-10-16 10:15:57 +0200 |
---|---|---|
committer | Neal H. Walfield <neal@gnu.org> | 2017-10-16 10:15:57 +0200 |
commit | 6f28ab1c4a3caae0eaf8650e4611f3ccf3a7f598 (patch) | |
tree | 25ca714030e182393dc6dbed527bfc43485e0d54 /src/openpgp |
openpgp: Add the start of a parser in nom.
Diffstat (limited to 'src/openpgp')
-rw-r--r-- | src/openpgp/build/build.rs | 1 | ||||
-rw-r--r-- | src/openpgp/build/mod.rs | 2 | ||||
-rw-r--r-- | src/openpgp/mod.rs | 8 | ||||
-rw-r--r-- | src/openpgp/openpgp.rs | 288 | ||||
-rw-r--r-- | src/openpgp/parse/literal-mode-b.asc | 1 | ||||
-rw-r--r-- | src/openpgp/parse/literal-mode-t-partial-body.asc | 99 | ||||
-rw-r--r-- | src/openpgp/parse/mod.rs | 2 | ||||
-rw-r--r-- | src/openpgp/parse/parse.rs | 541 | ||||
-rw-r--r-- | src/openpgp/parse/public-key.asc | bin | 0 -> 25148 bytes | |||
-rw-r--r-- | src/openpgp/parse/sig.asc | bin | 0 -> 310 bytes | |||
-rw-r--r-- | src/openpgp/serialize/mod.rs | 2 | ||||
-rw-r--r-- | src/openpgp/serialize/serialize.rs | 1 |
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 }); + |