summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--ffi/src/error.rs5
-rw-r--r--openpgp/build.rs6
-rw-r--r--openpgp/src/lib.rs9
-rw-r--r--openpgp/src/tpk/grammar.lalrpop257
-rw-r--r--openpgp/src/tpk/lexer.rs147
-rw-r--r--openpgp/src/tpk/mod.rs (renamed from openpgp/src/tpk.rs)1071
7 files changed, 1048 insertions, 448 deletions
diff --git a/.gitignore b/.gitignore
index d210def0..b790edcf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
Cargo.lock
.gdb_history
/openpgp/src/message/grammar.rs
+/openpgp/src/tpk/grammar.rs
diff --git a/ffi/src/error.rs b/ffi/src/error.rs
index 6e40de34..647a7a6e 100644
--- a/ffi/src/error.rs
+++ b/ffi/src/error.rs
@@ -118,6 +118,9 @@ pub enum Status {
/// Index out of range.
IndexOutOfRange = -23,
+
+ /// TPK not supported.
+ UnsupportedTPK = -24,
}
impl<'a> From<&'a failure::Error> for Status {
@@ -171,6 +174,8 @@ impl<'a> From<&'a failure::Error> for Status {
Status::MalformedTPK,
&openpgp::Error::IndexOutOfRange =>
Status::IndexOutOfRange,
+ &openpgp::Error::UnsupportedTPK(_) =>
+ Status::UnsupportedTPK,
}
}
diff --git a/openpgp/build.rs b/openpgp/build.rs
index b18f693b..76042853 100644
--- a/openpgp/build.rs
+++ b/openpgp/build.rs
@@ -2,8 +2,10 @@ extern crate lalrpop;
// Rerun if any of these files change:
#[allow(dead_code)]
-const SOURCE: &'static str
- = include_str!("src/message/grammar.lalrpop");
+const SOURCE: [ &'static str; 2 ]
+ = [ include_str!("src/message/grammar.lalrpop"),
+ include_str!("src/tpk/grammar.lalrpop"),
+ ];
fn main() {
lalrpop::process_root().unwrap();
diff --git a/openpgp/src/lib.rs b/openpgp/src/lib.rs
index b85e1c30..04ae2e67 100644
--- a/openpgp/src/lib.rs
+++ b/openpgp/src/lib.rs
@@ -212,6 +212,14 @@ pub enum Error {
#[fail(display = "Malformed TPK: {}", _0)]
MalformedTPK(String),
+ /// Unsupported TPK.
+ ///
+ /// This usually occurs, because the primary key is in an
+ /// unsupported format. In particular, Sequoia does not support
+ /// version 3 keys.
+ #[fail(display = "Unsupported TPK: {}", _0)]
+ UnsupportedTPK(String),
+
/// Index out of range.
#[fail(display = "Index out of range")]
IndexOutOfRange,
@@ -643,6 +651,7 @@ pub struct TPK {
userids: Vec<tpk::UserIDBinding>,
user_attributes: Vec<tpk::UserAttributeBinding>,
subkeys: Vec<tpk::SubkeyBinding>,
+ unknowns: Vec<tpk::UnknownBinding>,
}
/// An OpenPGP message.
diff --git a/openpgp/src/tpk/grammar.lalrpop b/openpgp/src/tpk/grammar.lalrpop
new file mode 100644
index 00000000..a89b3d9b
--- /dev/null
+++ b/openpgp/src/tpk/grammar.lalrpop
@@ -0,0 +1,257 @@
+// -*- mode: Rust; -*-
+
+use Error;
+use Signature;
+use UserID;
+use UserAttribute;
+use Key;
+use Unknown;
+use Packet;
+use TPK;
+
+use tpk::lexer;
+use tpk::lexer::{Token, Component};
+use tpk::{SubkeyBinding, UserIDBinding, UserAttributeBinding, UnknownBinding};
+
+use lalrpop_util::ParseError;
+
+grammar;
+
+// The parser is used in two ways: it can either be used to check
+// whether a sequence of packets forms a TPK, or to build a TPK from a
+// sequence of packets. In the former case, we only need the packet
+// tags; in the latter case, we also need the packets. To handle both
+// situations, the token includes the tag and an optional packet.
+// When invoking the parser, it is essential, that either *all* tokens
+// have no packet, or they all have a packet; mixing the two types of
+// tokens will result in a crash.
+
+pub TPK: Option<TPK> = {
+ <p:Primary> <c:OptionalComponents> =>? {
+ match p {
+ Some((Packet::PublicKey(key), _sigs))
+ | Some((Packet::SecretKey(key), _sigs)) => {
+ let c = c.unwrap();
+
+ let mut tpk = TPK {
+ primary: key,
+ subkeys: vec![],
+ userids: vec![],
+ user_attributes: vec![],
+ unknowns: vec![],
+ };
+
+ for c in c.into_iter() {
+ match c {
+ Component::SubkeyBinding(b) =>
+ tpk.subkeys.push(b),
+ Component::UserIDBinding(b) =>
+ tpk.userids.push(b),
+ Component::UserAttributeBinding(b) =>
+ tpk.user_attributes.push(b),
+ Component::UnknownBinding(b) =>
+ tpk.unknowns.push(b),
+ }
+ }
+
+ Ok(Some(tpk))
+ }
+ Some((Packet::Unknown(_unknown), _sigs)) => {
+ Err(ParseError::User {
+ error: Error::UnsupportedTPK(
+ "Unsupported primary key.".into())
+ })
+ }
+ None => {
+ // Just validating a message...
+ assert!(c.is_none() || c.unwrap().len() == 0);
+ Ok(None)
+ }
+ _ => unreachable!(),
+ }
+ }
+};
+
+Primary: Option<(Packet, Vec<Signature>)> = {
+ <pk:PrimaryKey> <sigs:OptionalSignatures> => {
+ if let Some(pk) = pk {
+ Some((pk, sigs.unwrap()))
+ } else {
+ // Just validating a message...
+ assert!(sigs.is_none() || sigs.unwrap().len() == 0);
+ None
+ }
+ }
+}
+
+PrimaryKey: Option<Packet> = {
+ <t:PUBLIC_KEY> => t.into(),
+ <t:SECRET_KEY> => t.into(),
+};
+
+OptionalSignatures: Option<Vec<Signature>> = {
+ => Some(vec![]),
+ <sigs:OptionalSignatures> <sig:SIGNATURE> => {
+ match sig {
+ Token::Signature(Some(Packet::Signature(sig))) => {
+ assert!(sigs.is_some());
+ let mut sigs = sigs.unwrap();
+
+ sigs.push(sig);
+ Some(sigs)
+ }
+ Token::Signature(Some(Packet::Unknown(_sig))) => {
+ // Ignore unsupported / bad signatures.
+ assert!(sigs.is_some());
+ sigs
+ }
+ // Just validating a message...
+ Token::Signature(None) => return None,
+ _ => unreachable!(),
+ }
+ }
+}
+
+OptionalComponents: Option<Vec<Component>> = {
+ => Some(vec![]),
+ <cs:OptionalComponents> <c:Component> => {
+ if let Some(c) = c {
+ let mut cs = cs.unwrap();
+ cs.push(c);
+ Some(cs)
+ } else {
+ // Just validating a message...
+ None
+ }
+ },
+}
+
+Component: Option<Component> = {
+ <key:Subkey> <sigs:OptionalSignatures> => {
+ match key {
+ Some(key) => {
+ let sigs = sigs.unwrap();
+
+ Some(Component::SubkeyBinding(SubkeyBinding {
+ subkey: key,
+ selfsigs: vec![],
+ certifications: sigs,
+ }))
+ },
+ // Just validating a message...
+ None => None,
+ }
+ },
+ <u:UserID> <sigs:OptionalSignatures> => {
+ match u {
+ Some(u) => {
+ let sigs = sigs.unwrap();
+
+ Some(Component::UserIDBinding(UserIDBinding {
+ userid: u,
+ selfsigs: vec![],
+ certifications: sigs,
+ }))
+ },
+ // Just validating a message...
+ None => None,
+ }
+ },
+ <u:UserAttribute> <sigs:OptionalSignatures> => {
+ match u {
+ Some(u) => {
+ let sigs = sigs.unwrap();
+
+ Some(Component::UserAttributeBinding(UserAttributeBinding {
+ user_attribute: u,
+ selfsigs: vec![],
+ certifications: sigs,
+ }))
+ },
+ // Just validating a message...
+ None => None,
+ }
+ },
+ <u:Unknown> <sigs:OptionalSignatures> => {
+ match u {
+ Some(u) => {
+ let sigs = sigs.unwrap();
+
+ Some(Component::UnknownBinding(UnknownBinding {
+ unknown: u,
+ sigs: sigs,
+ }))
+ },
+ // Just validating a message...
+ None => None,
+ }
+ },
+}
+
+Subkey: Option<Key> = {
+ <t:PUBLIC_SUBKEY> => {
+ match t.into() {
+ Some(Packet::PublicSubkey(key)) => Some(key),
+ // Just validating a message...
+ None => None,
+ _ => unreachable!(),
+ }
+ },
+ <t:SECRET_SUBKEY> => {
+ match t.into() {
+ Some(Packet::SecretSubkey(key)) => Some(key),
+ // Just validating a message...
+ None => None,
+ _ => unreachable!(),
+ }
+ },
+}
+
+UserID: Option<UserID> = {
+ <t:USERID> => {
+ match t.into() {
+ Some(Packet::UserID(u)) => Some(u),
+ // Just validating a message...
+ None => None,
+ _ => unreachable!(),
+ }
+ },
+}
+
+UserAttribute: Option<UserAttribute> = {
+ <t:USER_ATTRIBUTE> => {
+ match t.into() {
+ Some(Packet::UserAttribute(u)) => Some(u),
+ // Just validating a message...
+ None => None,
+ _ => unreachable!(),
+ }
+ },
+}
+
+Unknown: Option<Unknown> = {
+ <t:UNKNOWN> => {
+ match t.into() {
+ Some(Packet::Unknown(u)) => Some(u),
+ // Just validating a message...
+ None => None,
+ _ => unreachable!(),
+ }
+ },
+}
+
+extern {
+ type Location = usize;
+ type Error = Error;
+
+ enum lexer::Token {
+ PUBLIC_KEY => lexer::Token::PublicKey(_),
+ SECRET_KEY => lexer::Token::SecretKey(_),
+ PUBLIC_SUBKEY => lexer::Token::PublicSubkey(_),
+ SECRET_SUBKEY => lexer::Token::SecretSubkey(_),
+ USERID => lexer::Token::UserID(_),
+ USER_ATTRIBUTE => lexer::Token::UserAttribute(_),
+ SIGNATURE => lexer::Token::Signature(_),
+ UNKNOWN => lexer::Token::Unknown(_, _),
+ }
+}
diff --git a/openpgp/src/tpk/lexer.rs b/openpgp/src/tpk/lexer.rs
new file mode 100644
index 00000000..aed42021
--- /dev/null
+++ b/openpgp/src/tpk/lexer.rs
@@ -0,0 +1,147 @@
+use std::fmt;
+
+use Error;
+use Packet;
+use packet::Tag;
+use tpk::SubkeyBinding;
+use tpk::UserIDBinding;
+use tpk::UserAttributeBinding;
+use tpk::UnknownBinding;
+
+// The type of the parser's input.
+//
+// The parser iterators over tuples consisting of the token's starting
+// position, the token itself, and the token's ending position.
+pub(crate) type LexerItem<Tok, Loc, Error>
+ = ::std::result::Result<(Loc, Tok, Loc), Error>;
+
+/// The components of an OpenPGP Message.
+#[derive(Debug, Clone, PartialEq)]
+pub enum Token {
+ /// A `PublicKey` packet.
+ PublicKey(Option<Packet>),
+ /// A `SecretKey` packet.
+ SecretKey(Option<Packet>),
+
+ /// A `PublicSubkey` packet.
+ PublicSubkey(Option<Packet>),
+ /// A `SecretSubkey` packet.
+ SecretSubkey(Option<Packet>),
+
+ /// A `UserID` packet.
+ UserID(Option<Packet>),
+ /// A `UserAttribute` packet.
+ UserAttribute(Option<Packet>),
+
+ /// A `Signature` packet.
+ Signature(Option<Packet>),
+
+ /// An `Unknown` packet.
+ Unknown(Tag, Option<Packet>),
+}
+
+/// Internal data-structure used by the parser.
+///
+/// Due to the way the parser code is generated, it must be marked as
+/// public. But, since this module is not public, it will not
+/// actually be exported to used of the library.
+pub enum Component {
+ SubkeyBinding(SubkeyBinding),
+ UserIDBinding(UserIDBinding),
+ UserAttributeBinding(UserAttributeBinding),
+ UnknownBinding(UnknownBinding),
+}
+
+impl<'a> From<&'a Token> for Tag {
+ fn from(token: &'a Token) -> Self {
+ match token {
+ &Token::PublicKey(_) => Tag::PublicKey,
+ &Token::SecretKey(_) => Tag::SecretKey,
+ &Token::PublicSubkey(_) => Tag::PublicSubkey,
+ &Token::SecretSubkey(_) => Tag::SecretSubkey,
+ &Token::UserID(_) => Tag::UserID,
+ &Token::UserAttribute(_) => Tag::UserAttribute,
+ &Token::Signature(_) => Tag::Signature,
+ &Token::Unknown(tag, _) => tag,
+ }
+ }
+}
+
+impl From<Token> for Tag {
+ fn from(token: Token) -> Self {
+ (&token).into()
+ }
+}
+
+impl From<Token> for Option<Packet> {
+ fn from(token: Token) -> Self {
+ match token {
+ Token::PublicKey(p @ Some(_)) => p,
+ Token::SecretKey(p @ Some(_)) => p,
+ Token::PublicSubkey(p @ Some(_)) => p,
+ Token::SecretSubkey(p @ Some(_)) => p,
+ Token::UserID(p @ Some(_)) => p,
+ Token::UserAttribute(p @ Some(_)) => p,
+ Token::Signature(p @ Some(_)) => p,
+ Token::Unknown(_, p @ Some(_)) => p,
+
+ Token::PublicKey(None)
+ | Token::SecretKey(None)
+ | Token::PublicSubkey(None)
+ | Token::SecretSubkey(None)
+ | Token::UserID(None)
+ | Token::UserAttribute(None)
+ | Token::Signature(None)
+ | Token::Unknown(_, None)
+ => None,
+ }
+ }
+}
+
+impl From<Packet> for Option<Token> {
+ fn from(p: Packet) -> Self {
+ match p.tag() {
+ Tag::PublicKey => Some(Token::PublicKey(Some(p))),
+ Tag::SecretKey => Some(Token::SecretKey(Some(p))),
+ Tag::PublicSubkey => Some(Token::PublicSubkey(Some(p))),
+ Tag::SecretSubkey => Some(Token::SecretSubkey(Some(p))),
+ Tag::UserID => Some(Token::UserID(Some(p))),
+ Tag::UserAttribute => Some(Token::UserAttribute(Some(p))),
+ Tag::Signature => Some(Token::Signature(Some(p))),
+ t @ Tag::Unknown(_) => Some(Token::Unknown(t, Some(p))),
+ _ => None,
+ }
+ }
+}
+
+impl fmt::Display for Token {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(&format!("{:?}", self)[..])
+ }
+}
+
+pub(crate) struct Lexer<'input> {
+ iter: Box<Iterator<Item=(usize, &'input Token)> + 'input>,
+}
+
+impl<'input> Iterator for Lexer<'input> {
+ type Item = LexerItem<Token, usize, Error>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let n = self.iter.next().map(|(pos, tok)| (pos, tok.clone()));
+ if let Some((pos, tok)) = n {
+ Some(Ok((pos, tok, pos)))
+ } else {
+ None
+ }
+ }
+}
+
+impl<'input> Lexer<'input> {
+ /// Uses a raw sequence of tokens as input to the parser.
+ pub(crate) fn from_tokens(raw: &'input [Token]) -> Self {
+ Lexer {
+ iter: Box::new(raw.iter().enumerate())
+ }
+ }
+}
diff --git a/openpgp/src/tpk.rs b/openpgp/src/tpk/mod.rs
index 7eee917e..f5d6745f 100644
--- a/openpgp/src/tpk.rs
+++ b/openpgp/src/tpk/mod.rs
@@ -9,6 +9,7 @@ use std::mem;
use std::fmt;
use std::vec;
use time;
+use failure;
use {
Error,
@@ -18,6 +19,7 @@ use {
Key,
UserID,
UserAttribute,
+ Unknown,
Packet,
PacketPile,
TPK,
@@ -28,6 +30,269 @@ use parse::{PacketParserResult, PacketParser};
use serialize::{Serialize, SerializeKey};
use constants::PublicKeyAlgorithm;
+mod lexer;
+mod grammar;
+
+use self::lexer::Lexer;
+pub use self::lexer::Token;
+
+use lalrpop_util::ParseError;
+
+use self::grammar::TPKParser as TPKLowLevelParser;
+
+// Converts a ParseError<usize, Token, Error> to a
+// ParseError<usize, Tag, Error>.
+//
+// Justification: a Token is a tuple containing a Tag and a Packet.
+// This function essentially drops the Packet. Dropping the packet is
+// necessary, because packets are not async, but Fail, which we want
+// to convert ParseErrors to, is. Since we don't need the packet in
+// general anyways, changing the Token to a Tag is a simple and
+// sufficient fix. Unfortunately, this conversion is a bit ugly and
+// will break if lalrpop ever extends ParseError.
+fn parse_error_downcast(e: ParseError<usize, Token, Error>)
+ -> ParseError<usize, Tag, Error>
+{
+ match e {
+ ParseError::UnrecognizedToken {
+ token: Some((start, t, end)),
+ expected,
+ } => ParseError::UnrecognizedToken {
+ token: Some((start, t.into(), end)),
+ expected,
+ },
+ ParseError::UnrecognizedToken {
+ token: None,
+ expected,
+ } => ParseError::UnrecognizedToken {
+ token: None,
+ expected,
+ },
+
+ ParseError::ExtraToken {
+ token: (start, t, end),
+ } => ParseError::ExtraToken {
+ token: (start, t.into(), end),
+ },
+
+ ParseError::InvalidToken { location }
+ => ParseError::InvalidToken { location },
+
+ ParseError::User { error }
+ => ParseError::User { error },
+ }
+}
+
+fn parse_error_to_openpgp_error(e: ParseError<usize, Tag, Error>) -> Error
+{
+ match e {
+ ParseError::User { error } => error,
+ e => Error::MalformedTPK(format!("{}", e)),
+ }
+}
+
+/// Errors that TPKValidator::check may return.
+#[derive(Debug, Clone)]
+pub enum TPKParserError {
+ /// A parser error.
+ Parser(ParseError<usize, Tag, Error>),
+ /// An OpenPGP error.
+ OpenPGP(Error),
+}
+
+impl From<TPKParserError> for failure::Error {
+ fn from(err: TPKParserError) -> Self {
+ match err {
+ TPKParserError::Parser(p) => p.into(),
+ TPKParserError::OpenPGP(p) => p.into(),
+ }
+ }
+}
+
+/// Whether a packet sequence is a valid TPK.
+#[derive(Debug)]
+pub enum TPKValidity {
+ /// The packet sequence is a valid TPK.
+ TPK,
+ /// The packet sequence is a valid TPK prefix.
+ TPKPrefix,
+ /// The packet sequence is definitely not a TPK.
+ Error(failure::Error),
+}
+
+impl TPKValidity {
+ /// Returns whether the packet sequence is a valid TPK.
+ ///
+ /// Note: a `TPKValidator` will only return this after
+ /// `TPKValidator::finish` has been called.
+ pub fn is_tpk(&self) -> bool {
+ if let TPKValidity::TPK = self {
+ true
+ } else {
+ false
+ }
+ }
+
+ /// Returns whether the packet sequence is a valid TPK prefix.
+ ///
+ /// Note: a `TPKValidator` will only return this before
+ /// `TPKValidator::finish` has been called.
+ pub fn is_tpk_prefix(&self) -> bool {
+ if let TPKValidity::TPKPrefix = self {
+ true
+ } else {
+ false
+ }
+ }
+
+ /// Returns whether the packet sequence is definitely not a valid
+ /// TPK.
+ pub fn is_err(&self) -> bool {
+ if let TPKValidity::Error(_) = self {
+ true
+ } else {
+ false
+ }
+ }
+}
+
+/// Used to help validate that a packet sequence is a valid TPK.
+#[derive(Debug)]
+pub struct TPKValidator {
+ tokens: Vec<Token>,
+ finished: bool,
+
+ // If we know that the packet sequence is invalid.
+ error: Option<TPKParserError>,
+}
+
+impl Default for TPKValidator {
+ fn default() -> Self {
+ TPKValidator::new()
+ }
+}
+
+impl TPKValidator {
+ /// Instantiates a new `TPKValidator`.
+ pub fn new() -> Self {
+ TPKValidator {
+ tokens: vec![],
+ finished: false,
+ error: None,
+ }
+ }
+
+ /// Returns whether the packet sequence is a valid TPK.
+ ///
+ /// Note: a `TPKValidator` will only return this after
+ /// `TPKValidator::finish` has been called.
+ pub fn is_tpk(&self) -> bool {
+ self.check().is_tpk()
+ }
+
+ /// Returns whether the packet sequence forms a valid TPK
+ /// prefix.
+ ///
+ /// Note: a `TPKValidator` will only return this before
+ /// `TPKValidator::finish` has been called.
+ pub fn is_tpk_prefix(&self) -> bool {
+ self.check().is_tpk_prefix()
+ }
+
+ /// Returns whether the packet sequence is definitely not a valid
+ /// TPK.
+ pub fn is_err(&self) -> bool {
+ self.check().is_err()
+ }
+
+ /// Add the token `token` to the token stream.
+ pub fn push_token(&mut self, token: Token) {
+ assert!(!self.finished);
+
+ if self.error.is_some() {
+ return;
+ }
+
+ self.tokens.push(token);
+ }
+
+ /// Add a packet of type `tag` to the token stream.
+ pub fn push(&mut self, tag: Tag) {
+ let token = match tag {
+ Tag::PublicKey => Token::PublicKey(None),
+ Tag::SecretKey => Token::SecretKey(None),
+ Tag::PublicSubkey => Token::PublicSubkey(None),
+ Tag::SecretSubkey => Token::SecretSubkey(None),
+ Tag::UserID => Token::UserID(None),
+ Tag::UserAttribute => Token::UserAttribute(None),
+ Tag::Signature => Token::Signature(None),
+ _ => {
+ // Unknown token.
+ self.error = Some(TPKParserError::OpenPGP(
+ Error::MalformedMessage(
+ format!("Invalid OpenPGP message: unexpected packet: {:?}",
+ tag).into())));
+ self.tokens.clear();
+ return;
+ }
+ };
+
+ self.push_token(token)
+ }
+
+ /// Note that the entire message has been seen.
+ ///
+ /// This function may only be called once.
+ ///
+ /// Once called, this function will no longer return
+ /// `TPKValidity::TPKPrefix`.
+ pub fn finish(&mut self) {
+ assert!(!self.finished);
+ self.finished = true;
+ }
+
+ /// Returns whether the token stream corresponds to a valid
+ /// TPK.
+ ///
+ /// This returns a tri-state: if the packet sequence is a valid
+ /// TPK, it returns TPKValidity::TPK, if the packet sequence is
+ /// invalid, then it returns TPKValidity::Error. If the packet
+ /// sequence could be valid, then it returns
+ /// TPKValidity::TPKPrefix.
+ ///
+ /// Note: if TPKValidator::finish() *hasn't* been called, then
+ /// this function will only ever return either
+ /// TPKValidity::TPKPrefix or TPKValidity::Error. Once
+ /// TPKValidity::finish() has been called, then only
+ /// TPKValidity::TPK or TPKValidity::Bad will be called.
+ pub fn check(&self) -> TPKValidity {
+ if let Some(ref err) = self.error {
+ return TPKValidity::Error((*err).clone().into());
+ }
+
+ let r = TPKLowLevelParser::new().parse(
+ Lexer::from_tokens(&self.tokens[..]));
+
+ if self.finished {
+ match r {
+ Ok(_) => TPKValidity::TPK,
+ Err(err) =>
+ TPKValidity::Error(
+ TPKParserError::Parser(parse_error_downcast(err)).into()),
+ }
+ } else {
+ match r {
+ Ok(_) => TPKValidity::TPKPrefix,
+ Err(ParseError::UnrecognizedToken { token: None, .. }) =>
+ TPKValidity::TPKPrefix,
+ Err(err) =>
+ TPKValidity::Error(
+ TPKParserError::Parser(parse_error_downcast(err)).into()),
+ }
+ }
+ }
+}
+
const TRACE : bool = false;
/// A subkey and any associated signatures.
@@ -214,6 +479,14 @@ impl UserAttributeBinding {
}
}
+/// A User Attribute and any associated signatures.
+#[derive(Debug, Clone, PartialEq)]
+pub struct UnknownBinding {
+ unknown: Unknown,
+
+ sigs: Vec<Signature>,
+}
+
/// An iterator over all `Key`s (both the primary key and any subkeys)
/// in a TPK.
///
@@ -237,45 +510,6 @@ impl<'a> Iterator for KeyIter<'a> {
}
}
-// We use a state machine to extract a TPK from a sequence of OpenPGP
-// packets. These are the states.
-enum TPKParserState {
- Start,
- TPK,
- UserID(UserIDBinding),
- UserAttribute(UserAttributeBinding),
- Subkey(SubkeyBinding),
- End,
-}
-
-impl fmt::Debug for TPKParserState {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- &TPKParserState::Start => f.debug_struct("Start").finish(),
- &TPKParserState::TPK => f.debug_struct("TPK").finish(),
- &TPKParserState::UserID(ref binding) =>
- f.debug_struct("UserID")
- .field("userid", &binding.userid)
- .field("self-sigs", &binding.selfsigs.len())
- .field("certifications", &binding.certifications.len())
- .finish(),
- &TPKParserState::UserAttribute(ref binding) =>
- f.debug_struct("UserAttribute")
- .field("userid", &binding.user_attribute)
- .field("self-sigs", &binding.selfsigs.len())
- .field("certifications", &binding.certifications.len())
- .finish(),
- &TPKParserState::Subkey(ref binding) =>
- f.debug_struct("Subkey")
- .field("subkey", &binding.subkey)
- .field("self-sigs", &binding.selfsigs.len())
- .field("certifications", &binding.certifications.len())
- .finish(),
- &TPKParserState::End => f.debug_struct("End").finish(),
- }
- }
-}
-
// A TPKParser can read packets from either an Iterator or a
// PacketParser. Ideally, we would just take an iterator, but we
// want to be able to handle errors, which iterators hide.
@@ -323,11 +557,7 @@ enum PacketSource<'a, I: Iterator<Item=Packet>> {
pub struct TPKParser<'a, I: Iterator<Item=Packet>> {
source: PacketSource<'a, I>,
- state: TPKParserState,
- primary: Option<Key>,
- userids: Vec<UserIDBinding>,
- user_attributes: Vec<UserAttributeBinding>,
- subkeys: Vec<SubkeyBinding>,
+ packets: Vec<Packet>,
saw_error: bool,
@@ -338,11 +56