From e82e6f543eb536ac9463d64e04c1f7f6962aa2c9 Mon Sep 17 00:00:00 2001 From: "Neal H. Walfield" Date: Tue, 31 Mar 2020 09:43:34 +0200 Subject: openpgp: Implement Preferences for ValidCert. - Preferences should be implemented for ValidComponentAmalgamation and ValidCert, not ValidComponentAmalgamation and ValidKeyAmalgamation. - Adjust the Preferences trait since ValidCert doesn't implement ValidAmalgamation. --- openpgp/src/cert/amalgamation.rs | 45 +++++- openpgp/src/cert/key_amalgamation.rs | 9 -- openpgp/src/cert/mod.rs | 183 +++++++++++++++++++--- openpgp/tests/data/keys/different-preferences.asc | 52 ++++++ 4 files changed, 252 insertions(+), 37 deletions(-) create mode 100644 openpgp/tests/data/keys/different-preferences.asc (limited to 'openpgp') diff --git a/openpgp/src/cert/amalgamation.rs b/openpgp/src/cert/amalgamation.rs index 0fc44d98..79b49dcc 100644 --- a/openpgp/src/cert/amalgamation.rs +++ b/openpgp/src/cert/amalgamation.rs @@ -23,9 +23,15 @@ use crate::{ Result, policy::Policy, types::{ + AEADAlgorithm, + CompressionAlgorithm, + Features, + HashAlgorithm, + KeyFlags, + KeyServerPreferences, RevocationKey, RevocationStatus, - KeyFlags, + SymmetricAlgorithm, }, }; @@ -276,7 +282,7 @@ pub trait ValidAmalgamation<'a, C: 'a> /// A certificate's component and its associated data. #[derive(Debug, PartialEq)] -pub struct ComponentAmalgamation<'a, C>{ +pub struct ComponentAmalgamation<'a, C> { cert: &'a Cert, bundle: &'a ComponentBundle, } @@ -751,8 +757,39 @@ impl<'a, C> ValidAmalgamation<'a, C> for ValidComponentAmalgamation<'a, C> { } } -impl<'a, C> crate::cert::Preferences<'a, C> - for ValidComponentAmalgamation<'a, C> {} +impl<'a, C> crate::cert::Preferences<'a> + for ValidComponentAmalgamation<'a, C> +{ + fn preferred_symmetric_algorithms(&self) + -> Option<&'a [SymmetricAlgorithm]> { + self.map(|s| s.preferred_symmetric_algorithms()) + } + + fn preferred_hash_algorithms(&self) -> Option<&'a [HashAlgorithm]> { + self.map(|s| s.preferred_hash_algorithms()) + } + + fn preferred_compression_algorithms(&self) + -> Option<&'a [CompressionAlgorithm]> { + self.map(|s| s.preferred_compression_algorithms()) + } + + fn preferred_aead_algorithms(&self) -> Option<&'a [AEADAlgorithm]> { + self.map(|s| s.preferred_aead_algorithms()) + } + + fn key_server_preferences(&self) -> Option { + self.map(|s| s.key_server_preferences()) + } + + fn preferred_key_server(&self) -> Option<&'a [u8]> { + self.map(|s| s.preferred_key_server()) + } + + fn features(&self) -> Option { + self.map(|s| s.features()) + } +} #[cfg(test)] mod test { diff --git a/openpgp/src/cert/key_amalgamation.rs b/openpgp/src/cert/key_amalgamation.rs index d8e1a4b7..3b138340 100644 --- a/openpgp/src/cert/key_amalgamation.rs +++ b/openpgp/src/cert/key_amalgamation.rs @@ -890,15 +890,6 @@ impl<'a, P, R, R2> ValidKeyAmalgamation<'a, P, R, R2> } -impl<'a, P, R, R2> crate::cert::Preferences<'a, Key> - for ValidKeyAmalgamation<'a, P, R, R2> - where P: 'a + key::KeyParts, - R: 'a + key::KeyRole, - R2: Copy, - Self: Primary<'a, P, R>, -{ -} - #[cfg(test)] mod test { use crate::policy::StandardPolicy as P; diff --git a/openpgp/src/cert/mod.rs b/openpgp/src/cert/mod.rs index b6f12c2e..642f0d6d 100644 --- a/openpgp/src/cert/mod.rs +++ b/openpgp/src/cert/mod.rs @@ -43,7 +43,6 @@ use crate::types::{ }; pub mod amalgamation; -use amalgamation::ValidAmalgamation; mod builder; mod bindings; pub mod components; @@ -232,55 +231,40 @@ type UnknownBindings = ComponentBundles; /// on self signatures can be used to express preferences for /// algorithms and key management. Furthermore, the key holder's /// OpenPGP implementation can express its feature set. -pub trait Preferences<'a, C: 'a>: ValidAmalgamation<'a, C> { +pub trait Preferences<'a> { /// Returns symmetric algorithms that the key holder prefers. /// /// The algorithms are ordered according by the key holder's - /// preference. fn preferred_symmetric_algorithms(&self) - -> Option<&'a [SymmetricAlgorithm]> { - self.map(|s| s.preferred_symmetric_algorithms()) - } + -> Option<&'a [SymmetricAlgorithm]>; /// Returns hash algorithms that the key holder prefers. /// /// The algorithms are ordered according by the key holder's /// preference. - fn preferred_hash_algorithms(&self) -> Option<&'a [HashAlgorithm]> { - self.map(|s| s.preferred_hash_algorithms()) - } + fn preferred_hash_algorithms(&self) -> Option<&'a [HashAlgorithm]>; /// Returns compression algorithms that the key holder prefers. /// /// The algorithms are ordered according by the key holder's /// preference. fn preferred_compression_algorithms(&self) - -> Option<&'a [CompressionAlgorithm]> { - self.map(|s| s.preferred_compression_algorithms()) - } + -> Option<&'a [CompressionAlgorithm]>; /// Returns AEAD algorithms that the key holder prefers. /// /// The algorithms are ordered according by the key holder's /// preference. - fn preferred_aead_algorithms(&self) -> Option<&'a [AEADAlgorithm]> { - self.map(|s| s.preferred_aead_algorithms()) - } + fn preferred_aead_algorithms(&self) -> Option<&'a [AEADAlgorithm]>; /// Returns the key holder's keyserver preferences. - fn key_server_preferences(&self) -> Option { - self.map(|s| s.key_server_preferences()) - } + fn key_server_preferences(&self) -> Option; /// Returns the key holder's preferred keyserver for updates. - fn preferred_key_server(&self) -> Option<&'a [u8]> { - self.map(|s| s.preferred_key_server()) - } + fn preferred_key_server(&self) -> Option<&'a [u8]>; /// Returns the key holder's feature set. - fn features(&self) -> Option { - self.map(|s| s.features()) - } + fn features(&self) -> Option; } // DOC-HACK: To avoid having a top-level re-export of `Cert`, we move @@ -1511,6 +1495,36 @@ impl<'a> ValidCert<'a> { } } +macro_rules! impl_pref { + ($subpacket:ident, $rt:ty) => { + fn $subpacket(&self) -> Option<$rt> + { + // When addressed by the fingerprint or keyid, we first + // look on the primary User ID and then fall back to the + // direct key signature. We need to be careful to handle + // the case where there are no User IDs. + if let Ok(u) = self.primary_userid() { + u.$subpacket() + } else if let Ok(sig) = self.direct_key_signature() { + sig.$subpacket() + } else { + None + } + } + } +} + +impl<'a> crate::cert::Preferences<'a> for ValidCert<'a> +{ + impl_pref!(preferred_symmetric_algorithms, &'a [SymmetricAlgorithm]); + impl_pref!(preferred_hash_algorithms, &'a [HashAlgorithm]); + impl_pref!(preferred_compression_algorithms, &'a [CompressionAlgorithm]); + impl_pref!(preferred_aead_algorithms, &'a [AEADAlgorithm]); + impl_pref!(key_server_preferences, KeyServerPreferences); + impl_pref!(preferred_key_server, &'a [u8]); + impl_pref!(features, Features); +} + #[cfg(test)] mod test { use crate::serialize::Serialize; @@ -3436,4 +3450,125 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= assert_eq!(cert_at.keys().count(), 1); Ok(()) } + + #[test] + fn different_preferences() -> Result<()> { + use crate::cert::Preferences; + let p = &crate::policy::StandardPolicy::new(); + + // This key returns different preferences depending on how you + // address it. (It has two user ids and the user ids have + // different preference packets on their respective self + // signatures.) + + let cert = Cert::from_bytes( + crate::tests::key("different-preferences.asc"))?; + assert_eq!(cert.userids().count(), 2); + + if let Some(userid) = cert.userids().nth(0) { + assert_eq!(userid.userid().value(), + &b"Alice Confusion "[..]); + + let userid = userid.with_policy(p, None).expect("valid"); + + use crate::types::SymmetricAlgorithm::*; + assert_eq!(userid.preferred_symmetric_algorithms(), + Some(&[ AES256, AES192, AES128, TripleDES ][..])); + + use crate::types::HashAlgorithm::*; + assert_eq!(userid.preferred_hash_algorithms(), + Some(&[ SHA512, SHA384, SHA256, SHA224, SHA1 ][..])); + + use crate::types::CompressionAlgorithm::*; + assert_eq!(userid.preferred_compression_algorithms(), + Some(&[ Zlib, BZip2, Zip ][..])); + + assert_eq!(userid.preferred_aead_algorithms(), None); + + // assert_eq!(userid.key_server_preferences(), + // Some(KeyServerPreferences::new(&[]))); + + assert_eq!(userid.features(), + Some(Features::new(&[]).set_mdc(true))); + } else { + panic!("two user ids"); + } + + if let Some(userid) = cert.userids().nth(0) { + assert_eq!(userid.userid().value(), + &b"Alice Confusion "[..]); + + let userid = userid.with_policy(p, None).expect("valid"); + + use crate::types::SymmetricAlgorithm::*; + assert_eq!(userid.preferred_symmetric_algorithms(), + Some(&[ AES256, AES192, AES128, TripleDES ][..])); + + use crate::types::HashAlgorithm::*; + assert_eq!(userid.preferred_hash_algorithms(), + Some(&[ SHA512, SHA384, SHA256, SHA224, SHA1 ][..])); + + use crate::types::CompressionAlgorithm::*; + assert_eq!(userid.preferred_compression_algorithms(), + Some(&[ Zlib, BZip2, Zip ][..])); + + assert_eq!(userid.preferred_aead_algorithms(), None); + + assert_eq!(userid.key_server_preferences(), + Some(KeyServerPreferences::new(&[0x80]))); + + assert_eq!(userid.features(), + Some(Features::new(&[]).set_mdc(true))); + + // Using the certificate should choose the primary user + // id, which is this one (because it is lexicographically + // earlier). + let cert = cert.with_policy(p, None).expect("valid"); + assert_eq!(userid.preferred_symmetric_algorithms(), + cert.preferred_symmetric_algorithms()); + assert_eq!(userid.preferred_hash_algorithms(), + cert.preferred_hash_algorithms()); + assert_eq!(userid.preferred_compression_algorithms(), + cert.preferred_compression_algorithms()); + assert_eq!(userid.preferred_aead_algorithms(), + cert.preferred_aead_algorithms()); + assert_eq!(userid.key_server_preferences(), + cert.key_server_preferences()); + assert_eq!(userid.features(), + cert.features()); + } else { + panic!("two user ids"); + } + + if let Some(userid) = cert.userids().nth(1) { + assert_eq!(userid.userid().value(), + &b"Alice Confusion "[..]); + + let userid = userid.with_policy(p, None).expect("valid"); + + use crate::types::SymmetricAlgorithm::*; + assert_eq!(userid.preferred_symmetric_algorithms(), + Some(&[ AES192, AES256, AES128, TripleDES ][..])); + + use crate::types::HashAlgorithm::*; + assert_eq!(userid.preferred_hash_algorithms(), + Some(&[ SHA384, SHA512, SHA256, SHA224, SHA1 ][..])); + + use crate::types::CompressionAlgorithm::*; + assert_eq!(userid.preferred_compression_algorithms(), + Some(&[ BZip2, Zlib, Zip ][..])); + + assert_eq!(userid.preferred_aead_algorithms(), None); + + assert_eq!(userid.key_server_preferences(), + Some(KeyServerPreferences::new(&[0x80]))); + + assert_eq!(userid.features(), + Some(Features::new(&[]).set_mdc(true))); + } else { + panic!("two user ids"); + } + + Ok(()) + } } diff --git a/openpgp/tests/data/keys/different-preferences.asc b/openpgp/tests/data/keys/different-preferences.asc new file mode 100644 index 00000000..ebd04704 --- /dev/null +++ b/openpgp/tests/data/keys/different-preferences.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +Tg== +=URg9 +-----END PGP PUBLIC KEY BLOCK----- -- cgit v1.2.3