summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--openpgp/NEWS6
-rw-r--r--openpgp/src/cert.rs15
-rw-r--r--openpgp/src/cert/amalgamation/key.rs60
3 files changed, 79 insertions, 2 deletions
diff --git a/openpgp/NEWS b/openpgp/NEWS
index ba31c24e..cd56b1b7 100644
--- a/openpgp/NEWS
+++ b/openpgp/NEWS
@@ -8,6 +8,7 @@
- Sequoia now ignores some formatting errors when reading secret
keys. Being lenient in this case helps the user recover their
valuable key material.
+
- Previously, Sequoia would buffer packet bodies when mapping is
enabled in the parser, even if the packet parser is not
configured to buffer the bodies. This adds considerable
@@ -21,6 +22,11 @@
body in the map, but changes the default behavior. If you need
the old behavior, please do adjust your code to buffer unread
content.
+
+ - To increase compatibility with early v4 certificates, if there is
+ no key flags subpacket on either the active binding signature or
+ the active direct key signature, we infer the key flags from the
+ key's role and public key algorithm.
** New functionality
- crypto::SessionKey::as_protected
- types::AEADAlgorithm::GCM
diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs
index 0170b0e8..f1f233da 100644
--- a/openpgp/src/cert.rs
+++ b/openpgp/src/cert.rs
@@ -7267,12 +7267,23 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
let vcert = c.with_policy(&np, None)?;
assert_eq!(vcert.keys().subkeys().count(), 1);
- // XXX: Unfortunately, it being a v3 signature, the subkey has
- // no keyflags, limiting its usefulness for now.
+ // A v3 signature has no subpackets, so there are no key
+ // flags. But, we then consider the key role and public key
+ // algorithm.
+ assert_eq!(vcert.keys().for_signing().count(), 1);
+ assert_eq!(vcert.keys().for_transport_encryption().count(), 1);
// The subkey is interesting because it is bound using a v3
// signature.
assert_eq!(c.keys().subkeys().with_policy(&np, None).count(), 1);
+
+ // A v3 signature has no subpackets, so there are no key
+ // flags. But, we then consider the key role and public key
+ // algorithm.
+ assert_eq!(c.keys().with_policy(&np, None).for_signing().count(), 1);
+ assert_eq!(c.keys().with_policy(&np, None)
+ .for_transport_encryption().count(), 1);
+
Ok(())
}
}
diff --git a/openpgp/src/cert/amalgamation/key.rs b/openpgp/src/cert/amalgamation/key.rs
index fb6ce57b..f04010a4 100644
--- a/openpgp/src/cert/amalgamation/key.rs
+++ b/openpgp/src/cert/amalgamation/key.rs
@@ -1874,6 +1874,11 @@ impl<'a, P, R, R2> ValidKeyAmalgamation<'a, P, R, R2>
/// a key's flags may change depending on the policy and the
/// reference time.
///
+ /// To increase compatibility with early v4 certificates, if there
+ /// is no key flags subpacket on the considered signatures, we
+ /// infer the key flags from the key's role and public key
+ /// algorithm.
+ ///
/// [`Key Flags`]: https://tools.ietf.org/html/rfc4880#section-5.2.3.21
/// [SSS]: https://de.wikipedia.org/wiki/Shamir%E2%80%99s_Secret_Sharing
/// [Section 5.2.3.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.2.3.3
@@ -1898,6 +1903,61 @@ impl<'a, P, R, R2> ValidKeyAmalgamation<'a, P, R, R2>
/// ```
pub fn key_flags(&self) -> Option<KeyFlags> {
self.map(|s| s.key_flags())
+ .or_else(|| {
+ // There is no key flags subpacket. Match on the key
+ // role and algorithm and synthesize one. We do this
+ // to better support very early v4 certificates, where
+ // either the binding signature is a v3 signature and
+ // cannot contain subpackets, or it is a v4 signature,
+ // but the key's capabilities were implied by the
+ // public key algorithm.
+ use crate::types::PublicKeyAlgorithm;
+
+ // XXX: We cannot know whether this is a primary key
+ // or not because of
+ // https://gitlab.com/sequoia-pgp/sequoia/-/issues/1036
+ let is_primary = false;
+
+ // We only match on public key algorithms used at the
+ // time.
+ #[allow(deprecated)]
+ match (is_primary, self.key().pk_algo()) {
+ (true, PublicKeyAlgorithm::RSAEncryptSign) =>
+ Some(KeyFlags::empty()
+ .set_certification()
+ .set_transport_encryption()
+ .set_storage_encryption()
+ .set_signing()),
+
+ (true, _) =>
+ Some(KeyFlags::empty()
+ .set_certification()
+ .set_signing()),
+
+ (false, PublicKeyAlgorithm::RSAEncryptSign) =>
+ Some(KeyFlags::empty()
+ .set_transport_encryption()
+ .set_storage_encryption()
+ .set_signing()),
+
+ (false,
+ | PublicKeyAlgorithm::RSASign
+ | PublicKeyAlgorithm::DSA) =>
+ Some(KeyFlags::empty().set_signing()),
+
+ (false,
+ | PublicKeyAlgorithm::RSAEncrypt
+ | PublicKeyAlgorithm::ElGamalEncrypt
+ | PublicKeyAlgorithm::ElGamalEncryptSign) =>
+ Some(KeyFlags::empty()
+ .set_transport_encryption()
+ .set_storage_encryption()),
+
+ // Be conservative: newer algorithms don't get to
+ // benefit from implicit key flags.
+ (false, _) => None,
+ }
+ })
}
/// Returns whether the key has at least one of the specified key