summaryrefslogtreecommitdiffstats
path: root/openpgp
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2022-06-07 16:52:17 +0200
committerJustus Winter <justus@sequoia-pgp.org>2022-06-08 11:09:39 +0200
commit4a2bb0042e4d89252fcde0e1094c40bf249765c5 (patch)
treed594fe06357618329c41efef6e0a84167d1a27b7 /openpgp
parentb27e9e1d2ccc145dffb080d9e621699fd6111737 (diff)
openpgp: New error condition, UnsupportedCert2.
- In contrast to UnsupportedCert, this variant carries all the packets that we failed to parse into a cert. Notably, this includes primary keys that we don't understand. Keeping the packets with the errors allows us to at least roundtrip the packets.
Diffstat (limited to 'openpgp')
-rw-r--r--openpgp/NEWS3
-rw-r--r--openpgp/src/cert.rs6
-rw-r--r--openpgp/src/cert/parser/low_level/grammar.lalrpop25
-rw-r--r--openpgp/src/cert/parser/low_level/lexer.rs28
-rw-r--r--openpgp/src/cert/parser/mod.rs48
-rw-r--r--openpgp/src/lib.rs5
6 files changed, 84 insertions, 31 deletions
diff --git a/openpgp/NEWS b/openpgp/NEWS
index a69105d7..fd6611d0 100644
--- a/openpgp/NEWS
+++ b/openpgp/NEWS
@@ -4,7 +4,10 @@
* Changes in 1.10.0
** New functionality
+ - Error::UnsupportedCert2
- TryFrom<Packet> for Unknown
+** Deprecated functionality
+ - Error::UnsupportedCert, use Error::UnsupportedCert2 instead
* Changes in 1.9.0
** New functionality
- AEADAlgorithm::nonce_size replaces AEADAlgorithm::iv_size
diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs
index b9550210..005e6a41 100644
--- a/openpgp/src/cert.rs
+++ b/openpgp/src/cert.rs
@@ -4123,16 +4123,16 @@ mod test {
// v3 primary keys are not supported.
let cert = Cert::from_bytes(crate::tests::key("john-v3.pgp"));
- assert_match!(Error::UnsupportedCert(_)
+ assert_match!(Error::UnsupportedCert2(..)
= cert.err().unwrap().downcast::<Error>().unwrap());
let cert = Cert::from_bytes(crate::tests::key("john-v3-secret.pgp"));
- assert_match!(Error::UnsupportedCert(_)
+ assert_match!(Error::UnsupportedCert2(..)
= cert.err().unwrap().downcast::<Error>().unwrap());
// Lutz's key is a v3 key.
let cert = Cert::from_bytes(crate::tests::key("lutz.gpg"));
- assert_match!(Error::UnsupportedCert(_)
+ assert_match!(Error::UnsupportedCert2(..)
= cert.err().unwrap().downcast::<Error>().unwrap());
// v3 certifications are not supported
diff --git a/openpgp/src/cert/parser/low_level/grammar.lalrpop b/openpgp/src/cert/parser/low_level/grammar.lalrpop
index 59462c8c..c5146a77 100644
--- a/openpgp/src/cert/parser/low_level/grammar.lalrpop
+++ b/openpgp/src/cert/parser/low_level/grammar.lalrpop
@@ -83,11 +83,28 @@ pub Cert: Option<Cert> = {
Ok(Some(cert))
}
- Some((Packet::Unknown(unknown), _sigs)) => {
+ Some((Packet::Unknown(unknown), sigs)) => {
+ let mut packets: Vec<Packet> = Default::default();
+ packets.push(unknown.into());
+ for sig in sigs {
+ packets.push(sig.into());
+ }
+ for c in c.unwrap_or_default().into_iter() {
+ match c {
+ Component::SubkeyBundle(b) =>
+ b.into_packets().for_each(|p| packets.push(p)),
+ Component::UserIDBundle(b) =>
+ b.into_packets().for_each(|p| packets.push(p)),
+ Component::UserAttributeBundle(b) =>
+ b.into_packets().for_each(|p| packets.push(p)),
+ Component::UnknownBundle(b) =>
+ b.into_packets().for_each(|p| packets.push(p)),
+ }
+ }
Err(ParseError::User {
- error: Error::UnsupportedCert(format!(
- "Unsupported primary key: Unparsable {} ({:?}).",
- unknown.tag(), unknown).into())
+ error: Error::UnsupportedCert2(
+ "Unsupported primary key".into(),
+ packets),
})
}
None => {
diff --git a/openpgp/src/cert/parser/low_level/lexer.rs b/openpgp/src/cert/parser/low_level/lexer.rs
index 0838d2b1..8cd23c5b 100644
--- a/openpgp/src/cert/parser/low_level/lexer.rs
+++ b/openpgp/src/cert/parser/low_level/lexer.rs
@@ -112,20 +112,22 @@ impl From<Token> for Option<Packet> {
}
}
-impl From<Packet> for Option<Token> {
- fn from(p: Packet) -> Self {
+impl std::convert::TryFrom<Packet> for Token {
+ type Error = Packet;
+
+ fn try_from(p: Packet) -> std::result::Result<Self, Self::Error> {
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))),
- Tag::Trust => Some(Token::Trust(Some(p))),
- t @ Tag::Unknown(_) => Some(Token::Unknown(t, Some(p))),
- t @ Tag::Private(_) => Some(Token::Unknown(t, Some(p))),
- _ => None,
+ Tag::PublicKey => Ok(Token::PublicKey(Some(p))),
+ Tag::SecretKey => Ok(Token::SecretKey(Some(p))),
+ Tag::PublicSubkey => Ok(Token::PublicSubkey(Some(p))),
+ Tag::SecretSubkey => Ok(Token::SecretSubkey(Some(p))),
+ Tag::UserID => Ok(Token::UserID(Some(p))),
+ Tag::UserAttribute => Ok(Token::UserAttribute(Some(p))),
+ Tag::Signature => Ok(Token::Signature(Some(p))),
+ Tag::Trust => Ok(Token::Trust(Some(p))),
+ t @ Tag::Unknown(_) => Ok(Token::Unknown(t, Some(p))),
+ t @ Tag::Private(_) => Ok(Token::Unknown(t, Some(p))),
+ _ => Err(p),
}
}
}
diff --git a/openpgp/src/cert/parser/mod.rs b/openpgp/src/cert/parser/mod.rs
index e031ec48..f7b3289a 100644
--- a/openpgp/src/cert/parser/mod.rs
+++ b/openpgp/src/cert/parser/mod.rs
@@ -792,21 +792,47 @@ impl<'a> CertParser<'a> {
self.packets.push(pk);
}
- let packets = orig.packets.len();
- t!("Finalizing certificate with {} packets", packets);
- let tokens = orig.packets
- .into_iter()
- .filter_map(|p| p.into())
- .collect::<Vec<Token>>();
- t!("{} tokens: {:?}", tokens.len(), tokens);
- if tokens.len() != packets {
+ let n_packets = orig.packets.len();
+ t!("Finalizing certificate with {} packets", n_packets);
+
+ // Convert to tokens, but preserve packets if it fails.
+ use std::convert::TryInto;
+ let mut failed = false;
+ let mut packets: Vec<Packet> = Vec::with_capacity(0);
+ let mut tokens: Vec<Token> = Vec::with_capacity(n_packets);
+ for p in orig.packets {
+ if failed {
+ // Just stash the packet.
+ packets.push(p);
+ } else {
+ match p.try_into() {
+ Ok(t) => tokens.push(t),
+ Err(p) => {
+ // Conversion failed. Revert the whole process.
+ packets.reserve(n_packets);
+ for t in tokens.drain(..) {
+ packets.push({
+ let p: Option<Packet> = t.into();
+ p.expect("token created with packet")
+ });
+ }
+ packets.push(p);
+ failed = true;
+ },
+ }
+ }
+ }
+
+ if failed {
// There was at least one packet that doesn't belong in a
// Cert. Fail now.
- let err = Error::UnsupportedCert(
- "Packet sequence includes non-Cert packets.".into());
+ let err = Error::UnsupportedCert2(
+ "Packet sequence includes non-Cert packets.".into(),
+ packets);
t!("Invalid certificate: {}", err);
return Err(err.into());
}
+ t!("{} tokens: {:?}", tokens.len(), tokens);
let certo = match CertLowLevelParser::new()
.parse(Lexer::from_tokens(&tokens))
@@ -861,7 +887,7 @@ impl<'a> CertParser<'a> {
t!("Returning {:?}, constructed from {} packets",
certo.as_ref().map(|c| c.fingerprint()),
- packets);
+ n_packets);
Ok(certo)
}
diff --git a/openpgp/src/lib.rs b/openpgp/src/lib.rs
index 11878174..4ea09661 100644
--- a/openpgp/src/lib.rs
+++ b/openpgp/src/lib.rs
@@ -296,6 +296,11 @@ pub enum Error {
/// unsupported format. In particular, Sequoia does not support
/// version 3 keys.
#[error("Unsupported Cert: {0}")]
+ UnsupportedCert2(String, Vec<Packet>),
+
+ /// Unsupported Cert, deprecated version.
+ #[deprecated(since = "1.10.0", note = "Use UnsupportedCert2 instead.")]
+ #[error("Unsupported Cert: {0}")]
UnsupportedCert(String),
/// Index out of range.