diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2023-08-22 10:14:47 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2024-03-13 10:59:08 +0100 |
commit | f92e298f9ac1785d29e93d86ce0aba51b250433e (patch) | |
tree | 7c6cd23152b1da3ee00437775683b7b4632a4fe5 | |
parent | ee37f1d7ff2747e799064e07f859f30942ec8e06 (diff) |
openpgp: Implement the PreferredAEADCiphersuites subpacket.
-rw-r--r-- | openpgp/examples/statistics.rs | 23 | ||||
-rw-r--r-- | openpgp/src/cert.rs | 27 | ||||
-rw-r--r-- | openpgp/src/cert/amalgamation.rs | 7 | ||||
-rw-r--r-- | openpgp/src/packet/signature.rs | 1 | ||||
-rw-r--r-- | openpgp/src/packet/signature/subpacket.rs | 113 | ||||
-rw-r--r-- | openpgp/src/parse.rs | 15 | ||||
-rw-r--r-- | openpgp/src/serialize.rs | 7 |
7 files changed, 159 insertions, 34 deletions
diff --git a/openpgp/examples/statistics.rs b/openpgp/examples/statistics.rs index 4382a924..48a16dbf 100644 --- a/openpgp/examples/statistics.rs +++ b/openpgp/examples/statistics.rs @@ -69,6 +69,8 @@ fn main() -> openpgp::Result<()> { Default::default(); let mut p_comp: HashMap<Vec<CompressionAlgorithm>, usize> = Default::default(); + let mut p_aead_ciphersuites: HashMap<Vec<(SymmetricAlgorithm, AEADAlgorithm)>, usize> = + Default::default(); let mut p_aead: HashMap<Vec<AEADAlgorithm>, usize> = Default::default(); @@ -210,6 +212,13 @@ fn main() -> openpgp::Result<()> { } else { p_comp.insert(a.clone(), 1); }, + SubpacketValue::PreferredAEADCiphersuites(a) + => + if let Some(count) = p_aead_ciphersuites.get_mut(a) { + *count += 1; + } else { + p_aead_ciphersuites.insert(a.clone(), 1); + }, SubpacketValue::PreferredAEADAlgorithms(a) => if let Some(count) = p_aead.get_mut(a) { @@ -528,6 +537,20 @@ fn main() -> openpgp::Result<()> { } } + if !p_aead_ciphersuites.is_empty() { + println!(); + println!("# PreferredAEADCiphersuites statistics"); + println!(); + println!("{:>70} {:>9}", "", "count",); + println!("----------------------------------------\ + ----------------------------------------"); + + for (a, n) in p_aead_ciphersuites.iter() { + let a = format!("{:?}", a); + println!("{:>70} {:>9}", &a[1..a.len()-1], n); + } + } + if !p_aead.is_empty() { println!(); println!("# PreferredAEADAlgorithms statistics"); diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs index b35fd7eb..acb4e7ab 100644 --- a/openpgp/src/cert.rs +++ b/openpgp/src/cert.rs @@ -472,6 +472,14 @@ pub trait Preferences<'a>: seal::Sealed { fn preferred_compression_algorithms(&self) -> Option<&'a [CompressionAlgorithm]>; + /// Returns the supported AEAD ciphersuites ordered by preference. + /// + /// The algorithms are ordered according by the certificate holder's + /// preference. + fn preferred_aead_ciphersuites( + &self) + -> Option<&'a [(SymmetricAlgorithm, AEADAlgorithm)]>; + /// Returns the supported AEAD algorithms ordered by preference. /// /// The algorithms are ordered according by the certificate holder's @@ -4465,6 +4473,7 @@ impl<'a> 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_ciphersuites, &'a [(SymmetricAlgorithm, AEADAlgorithm)]); impl_pref!(preferred_aead_algorithms, &'a [AEADAlgorithm]); impl_pref!(key_server_preferences, KeyServerPreferences); impl_pref!(preferred_key_server, &'a [u8]); @@ -6752,9 +6761,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= assert_eq!(userid.preferred_compression_algorithms(), Some(&[ Zlib, BZip2, Zip ][..])); - #[allow(deprecated)] { - assert_eq!(userid.preferred_aead_algorithms(), None); - } + assert_eq!(userid.preferred_aead_ciphersuites(), None); // assert_eq!(userid.key_server_preferences(), // Some(KeyServerPreferences::new(&[]))); @@ -6783,9 +6790,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= assert_eq!(userid.preferred_compression_algorithms(), Some(&[ Zlib, BZip2, Zip ][..])); - #[allow(deprecated)] { - assert_eq!(userid.preferred_aead_algorithms(), None); - } + assert_eq!(userid.preferred_aead_ciphersuites(), None); assert_eq!(userid.key_server_preferences(), Some(KeyServerPreferences::new(&[0x80]))); @@ -6803,10 +6808,8 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= cert.preferred_hash_algorithms()); assert_eq!(userid.preferred_compression_algorithms(), cert.preferred_compression_algorithms()); - #[allow(deprecated)] { - assert_eq!(userid.preferred_aead_algorithms(), - cert.preferred_aead_algorithms()); - } + assert_eq!(userid.preferred_aead_ciphersuites(), + cert.preferred_aead_ciphersuites()); assert_eq!(userid.key_server_preferences(), cert.key_server_preferences()); assert_eq!(userid.features(), @@ -6833,9 +6836,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= assert_eq!(userid.preferred_compression_algorithms(), Some(&[ BZip2, Zlib, Zip ][..])); - #[allow(deprecated)] { - assert_eq!(userid.preferred_aead_algorithms(), None); - } + assert_eq!(userid.preferred_aead_ciphersuites(), None); assert_eq!(userid.key_server_preferences(), Some(KeyServerPreferences::new(&[0x80]))); diff --git a/openpgp/src/cert/amalgamation.rs b/openpgp/src/cert/amalgamation.rs index a36699aa..9548bd11 100644 --- a/openpgp/src/cert/amalgamation.rs +++ b/openpgp/src/cert/amalgamation.rs @@ -2303,6 +2303,13 @@ impl<'a, C> crate::cert::Preferences<'a> self.map(|s| s.preferred_compression_algorithms()) } + fn preferred_aead_ciphersuites( + &self) + -> Option<&'a [(SymmetricAlgorithm, AEADAlgorithm)]> + { + self.map(|s| s.preferred_aead_ciphersuites()) + } + fn preferred_aead_algorithms(&self) -> Option<&'a [AEADAlgorithm]> { #[allow(deprecated)] self.map(|s| s.preferred_aead_algorithms()) diff --git a/openpgp/src/packet/signature.rs b/openpgp/src/packet/signature.rs index 41f56453..e3e58f88 100644 --- a/openpgp/src/packet/signature.rs +++ b/openpgp/src/packet/signature.rs @@ -2569,6 +2569,7 @@ impl crate::packet::Signature { | PreferredAEADAlgorithms | IntendedRecipient | AttestedCertifications + | PreferredAEADCiphersuites | Reserved(_) => false, Issuer diff --git a/openpgp/src/packet/signature/subpacket.rs b/openpgp/src/packet/signature/subpacket.rs index 79b61fdd..9be2842c 100644 --- a/openpgp/src/packet/signature/subpacket.rs +++ b/openpgp/src/packet/signature/subpacket.rs @@ -325,6 +325,27 @@ pub enum SubpacketTag { /// /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 AttestedCertifications, + + /// The AEAD Ciphersuites that the certificate holder prefers. + /// + /// A series of paired algorithm identifiers indicating how the + /// keyholder prefers to receive version 2 Symmetrically Encrypted + /// Integrity Protected Data. Each pair of octets indicates a + /// combination of a symmetric cipher and an AEAD mode that the + /// key holder prefers to use. + /// + /// It is assumed that only the combinations of algorithms listed + /// are supported by the recipient's software, with the exception + /// of the mandatory-to-implement combination of AES-128 and OCB. + /// If AES-128 and OCB are not found in the subpacket, it is + /// implicitly listed at the end. + /// + /// See [Section 5.2.3.15 of draft-ietf-openpgp-crypto-refresh-10] + /// for details. + /// + /// [Section 5.2.3.15 of draft-ietf-openpgp-crypto-refresh-10]: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-preferred-aead-ciphersuites + PreferredAEADCiphersuites, + /// Reserved subpacket tag. Reserved(u8), /// Private subpacket tag. @@ -375,6 +396,7 @@ impl From<u8> for SubpacketTag { 34 => SubpacketTag::PreferredAEADAlgorithms, 35 => SubpacketTag::IntendedRecipient, 37 => SubpacketTag::AttestedCertifications, + 39 => SubpacketTag::PreferredAEADCiphersuites, 0| 1| 8| 13| 14| 15| 17| 18| 19 | 38 => SubpacketTag::Reserved(u), 100..=110 => SubpacketTag::Private(u), _ => SubpacketTag::Unknown(u), @@ -414,6 +436,7 @@ impl From<SubpacketTag> for u8 { SubpacketTag::PreferredAEADAlgorithms => 34, SubpacketTag::IntendedRecipient => 35, SubpacketTag::AttestedCertifications => 37, + SubpacketTag::PreferredAEADCiphersuites => 39, SubpacketTag::Reserved(u) => u, SubpacketTag::Private(u) => u, SubpacketTag::Unknown(u) => u, @@ -422,7 +445,7 @@ impl From<SubpacketTag> for u8 { } #[allow(deprecated)] -const SUBPACKET_TAG_VARIANTS: [SubpacketTag; 28] = [ +const SUBPACKET_TAG_VARIANTS: [SubpacketTag; 29] = [ SubpacketTag::SignatureCreationTime, SubpacketTag::SignatureExpirationTime, SubpacketTag::ExportableCertification, @@ -451,6 +474,7 @@ const SUBPACKET_TAG_VARIANTS: [SubpacketTag; 28] = [ SubpacketTag::PreferredAEADAlgorithms, SubpacketTag::IntendedRecipient, SubpacketTag::AttestedCertifications, + SubpacketTag::PreferredAEADCiphersuites, ]; impl SubpacketTag { @@ -1687,6 +1711,26 @@ pub enum SubpacketValue { /// /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 AttestedCertifications(Vec<Box<[u8]>>), + + /// The AEAD Ciphersuites that the certificate holder prefers. + /// + /// A series of paired algorithm identifiers indicating how the + /// keyholder prefers to receive version 2 Symmetrically Encrypted + /// Integrity Protected Data. Each pair of octets indicates a + /// combination of a symmetric cipher and an AEAD mode that the + /// key holder prefers to use. + /// + /// It is assumed that only the combinations of algorithms listed + /// are supported by the recipient's software, with the exception + /// of the mandatory-to-implement combination of AES-128 and OCB. + /// If AES-128 and OCB are not found in the subpacket, it is + /// implicitly listed at the end. + /// + /// See [Section 5.2.3.15 of draft-ietf-openpgp-crypto-refresh-10] + /// for details. + /// + /// [Section 5.2.3.15 of draft-ietf-openpgp-crypto-refresh-10]: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-preferred-aead-ciphersuites + PreferredAEADCiphersuites(Vec<(SymmetricAlgorithm, AEADAlgorithm)>), } assert_send_and_sync!(SubpacketValue); @@ -1698,7 +1742,7 @@ impl ArbitraryBounded for SubpacketValue { loop { #[allow(deprecated)] - break match gen_arbitrary_from_range(0..26, g) { + break match gen_arbitrary_from_range(0..27, g) { 0 => SignatureCreationTime(Arbitrary::arbitrary(g)), 1 => SignatureExpirationTime(Arbitrary::arbitrary(g)), 2 => ExportableCertification(Arbitrary::arbitrary(g)), @@ -1737,6 +1781,7 @@ impl ArbitraryBounded for SubpacketValue { 23 => IssuerFingerprint(Arbitrary::arbitrary(g)), 24 => PreferredAEADAlgorithms(Arbitrary::arbitrary(g)), 25 => IntendedRecipient(Arbitrary::arbitrary(g)), + 26 => PreferredAEADCiphersuites(Arbitrary::arbitrary(g)), _ => unreachable!(), } } @@ -1785,6 +1830,8 @@ impl SubpacketValue { SubpacketTag::PreferredAEADAlgorithms, IntendedRecipient(_) => SubpacketTag::IntendedRecipient, AttestedCertifications(_) => SubpacketTag::AttestedCertifications, + PreferredAEADCiphersuites(_) => + SubpacketTag::PreferredAEADCiphersuites, Unknown { tag, .. } => *tag, } } @@ -3259,18 +3306,16 @@ impl SubpacketAreas { } } - /// Returns the value of the Preferred AEAD Algorithms subpacket. + /// Returns the value of the Preferred AEAD Ciphersuites subpacket. /// - /// The [Preferred AEAD Algorithms subpacket] indicates what AEAD - /// algorithms the key holder prefers ordered by preference. If - /// this is set, then the AEAD feature flag should in the + /// The [Preferred AEAD Ciphersuites subpacket] indicates what + /// AEAD ciphersuites (i.e. pairs of symmetric algorithm and AEAD + /// algorithm) the key holder prefers ordered by preference. If + /// this is set, then the SEIPDv2 feature flag should in the /// [Features subpacket] should also be set. /// - /// Note: because support for AEAD has not yet been standardized, - /// we recommend not yet advertising support for it. - /// - /// [Preferred AEAD Algorithms subpacket]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09.html#section-5.2.3.8 - /// [Features subpacket]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09.html#section-5.2.3.25 + /// [Preferred AEAD Ciphersuites subpacket]: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-preferred-aead-ciphersuites + /// [Features subpacket]: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#features-subpacket /// /// This subpacket is a type of preference. When looking up a /// preference, an OpenPGP implementation should first look for @@ -3294,7 +3339,25 @@ impl SubpacketAreas { /// Note: if the signature contains multiple instances of this /// subpacket in the hashed subpacket area, the last one is /// returned. - #[deprecated] + pub fn preferred_aead_ciphersuites(&self) + -> Option<&[(SymmetricAlgorithm, AEADAlgorithm)]> + { + if let Some(sb) = self.subpacket( + SubpacketTag::PreferredAEADCiphersuites) + { + if let SubpacketValue::PreferredAEADCiphersuites(v) + = &sb.value + { + Some(v) + } else { + None + } + } else { + None + } + } + + /// Returns the value of the Preferred AEAD Algorithms subpacket. pub fn preferred_aead_algorithms(&self) -> Option<&[AEADAlgorithm]> { // array of one-octet values @@ -6954,7 +7017,19 @@ impl signature::SignatureBuilder { /// # Ok(()) /// # } /// ``` - #[deprecated] + pub fn set_preferred_aead_ciphersuites( + mut self, + preferences: Vec<(SymmetricAlgorithm, AEADAlgorithm)>) + -> Result<Self> + { + self.hashed_area.replace(Subpacket::new( + SubpacketValue::PreferredAEADCiphersuites(preferences), + false)?)?; + + Ok(self) + } + + /// Sets the Preferred AEAD Algorithms subpacket. pub fn set_preferred_aead_algorithms(mut self, preferences: Vec<AEADAlgorithm>) -> Result<Self> @@ -7397,16 +7472,12 @@ fn accessors() { assert_eq!(sig_.issuer_fingerprints().collect::<Vec<_>>(), vec![ &fp ]); - let pref = vec![AEADAlgorithm::EAX, - AEADAlgorithm::OCB]; - #[allow(deprecated)] { - sig = sig.set_preferred_aead_algorithms(pref.clone()).unwrap(); - } + let pref = vec![(SymmetricAlgorithm::AES128, AEADAlgorithm::EAX), + (SymmetricAlgorithm::AES128, AEADAlgorithm::OCB)]; + sig = sig.set_preferred_aead_ciphersuites(pref.clone()).unwrap(); let sig_ = sig.clone().sign_hash(&mut keypair, hash.clone()).unwrap(); - #[allow(deprecated)] { - assert_eq!(sig_.preferred_aead_algorithms(), Some(&pref[..])); - } + assert_eq!(sig_.preferred_aead_ciphersuites(), Some(&pref[..])); let fps = vec![ Fingerprint::from_bytes(b"aaaaaaaaaaaaaaaaaaaa"), diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs index 05598ea7..f7daffe7 100644 --- a/openpgp/src/parse.rs +++ b/openpgp/src/parse.rs @@ -1859,6 +1859,21 @@ impl Subpacket { bytes.chunks(digest_size).map(Into::into).collect()) } }, + + SubpacketTag::PreferredAEADCiphersuites => { + if len % 2 != 0 { + return Err(Error::BadSignature( + "Wrong number of bytes in preferred AEAD \ + Ciphersuites subpacket" + .into()).into()); + } + + SubpacketValue::PreferredAEADCiphersuites( + php.parse_bytes("pref aead ciphersuites", len)? + .chunks(2).map(|o| (o[0].into(), + o[1].into())).collect()) + }, + SubpacketTag::Reserved(_) | SubpacketTag::PlaceholderForBackwardCompatibility | SubpacketTag::Private(_) diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs index 1bdd062f..a45e84dc 100644 --- a/openpgp/src/serialize.rs +++ b/openpgp/src/serialize.rs @@ -1527,6 +1527,12 @@ impl Marshal for SubpacketValue { o.write_all(digest)?; } }, + + PreferredAEADCiphersuites(p) => + for (symm, aead) in p { + o.write_all(&[(*symm).into(), (*aead).into()])?; + }, + Unknown { body, .. } => o.write_all(body)?, } @@ -1569,6 +1575,7 @@ impl MarshalInto for SubpacketValue { 1 + (fp as &dyn MarshalInto).serialized_len(), AttestedCertifications(digests) => digests.iter().map(|d| d.len()).sum(), + PreferredAEADCiphersuites(c) => c.len() * 2, Unknown { body, .. } => body.len(), } } |