From 743288ca7a998211b34064a57b0215a2ed337902 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 25 Apr 2023 18:35:48 +0200 Subject: wip pkeskv6 --- openpgp/examples/decrypt-with.rs | 4 +- openpgp/examples/generate-encrypt-decrypt.rs | 2 +- openpgp/examples/reply-encrypted.rs | 10 +- openpgp/src/keyhandle.rs | 10 ++ openpgp/src/packet/mod.rs | 82 +++++++++--- openpgp/src/packet/pkesk.rs | 9 +- openpgp/src/packet/pkesk/v6.rs | 189 +++++++++++++++++++++++++++ openpgp/src/packet/prelude.rs | 5 +- openpgp/src/packet/skesk.rs | 11 +- openpgp/src/parse.rs | 95 +++++++++++--- openpgp/src/parse/stream.rs | 46 ++++--- openpgp/src/policy.rs | 8 +- openpgp/src/serialize.rs | 59 +++++++++ openpgp/src/serialize/stream.rs | 40 ++++-- 14 files changed, 486 insertions(+), 84 deletions(-) create mode 100644 openpgp/src/packet/pkesk/v6.rs diff --git a/openpgp/examples/decrypt-with.rs b/openpgp/examples/decrypt-with.rs index 20382d3a..c5cfe17b 100644 --- a/openpgp/examples/decrypt-with.rs +++ b/openpgp/examples/decrypt-with.rs @@ -91,12 +91,12 @@ impl DecryptionHelper for Helper { sym_algo: Option, mut decrypt: D) -> openpgp::Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { // Try each PKESK until we succeed. let mut recipient = None; for pkesk in pkesks { - if let Some((fp, pair)) = self.keys.get_mut(pkesk.recipient()) { + if let Some((fp, pair)) = self.keys.get_mut(&KeyID::from(pkesk.recipient())) { if pkesk.decrypt(pair, sym_algo) .map(|(algo, session_key)| decrypt(algo, &session_key)) .unwrap_or(false) diff --git a/openpgp/examples/generate-encrypt-decrypt.rs b/openpgp/examples/generate-encrypt-decrypt.rs index bd8e1dfa..e080c423 100644 --- a/openpgp/examples/generate-encrypt-decrypt.rs +++ b/openpgp/examples/generate-encrypt-decrypt.rs @@ -121,7 +121,7 @@ impl<'a> DecryptionHelper for Helper<'a> { sym_algo: Option, mut decrypt: D) -> openpgp::Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { let key = self.secret.keys().unencrypted_secret() .with_policy(self.policy, None) diff --git a/openpgp/examples/reply-encrypted.rs b/openpgp/examples/reply-encrypted.rs index b0d521c5..0f6a09ee 100644 --- a/openpgp/examples/reply-encrypted.rs +++ b/openpgp/examples/reply-encrypted.rs @@ -88,7 +88,7 @@ pub fn main() -> openpgp::Result<()> { .context("Decryption failed")?; let (algo, sk, pkesks) = decryptor.into_helper().recycling_bin.unwrap(); - eprintln!("- Reusing ({}, {}) with {} PKESK packets", + eprintln!("- Reusing ({:?}, {}) with {} PKESK packets", algo, openpgp::fmt::hex::encode(&sk), pkesks.len()); // Compose a writer stack corresponding to the output format and @@ -105,7 +105,7 @@ pub fn main() -> openpgp::Result<()> { } // We want to encrypt a literal data packet. - let message = Encryptor::with_session_key(message, algo, sk)? + let message = Encryptor::with_session_key(message, algo.expect("XXX seipdv2"), sk)? .build().context("Failed to create encryptor")?; let mut message = LiteralWriter::new(message).build() @@ -128,7 +128,7 @@ pub fn main() -> openpgp::Result<()> { /// verification policy. struct Helper { keys: HashMap, - recycling_bin: Option<(SymmetricAlgorithm, SessionKey, Vec)>, + recycling_bin: Option<(Option, SessionKey, Vec)>, } impl Helper { @@ -161,13 +161,13 @@ impl DecryptionHelper for Helper { sym_algo: Option, mut decrypt: D) -> openpgp::Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { // Try each PKESK until we succeed. let mut recipient = None; let mut encryption_context = None; for pkesk in pkesks { - if let Some((fp, pair)) = self.keys.get_mut(pkesk.recipient()) { + if let Some((fp, pair)) = self.keys.get_mut(&KeyID::from(pkesk.recipient())) { if pkesk.decrypt(pair, sym_algo) .map(|(algo, session_key)| { let success = decrypt(algo, &session_key); diff --git a/openpgp/src/keyhandle.rs b/openpgp/src/keyhandle.rs index 9ce528ba..092a0aa2 100644 --- a/openpgp/src/keyhandle.rs +++ b/openpgp/src/keyhandle.rs @@ -126,6 +126,16 @@ impl From for KeyID { } } +impl From> for KeyID { + fn from(i: Option) -> Self { + match i { + Some(KeyHandle::Fingerprint(i)) => i.into(), + Some(KeyHandle::KeyID(i)) => i, + None => KeyID::wildcard(), + } + } +} + impl From<&KeyHandle> for KeyID { fn from(i: &KeyHandle) -> Self { match i { diff --git a/openpgp/src/packet/mod.rs b/openpgp/src/packet/mod.rs index c004155a..8e0be2c7 100644 --- a/openpgp/src/packet/mod.rs +++ b/openpgp/src/packet/mod.rs @@ -170,11 +170,14 @@ pub mod prelude; use crate::{ crypto::{ + Decryptor, KeyPair, Password, + SessionKey, }, types::{ SignatureType, + SymmetricAlgorithm, PublicKeyAlgorithm, HashAlgorithm, }, @@ -474,7 +477,8 @@ impl Deref for Packet { Packet::UserAttribute(ref packet) => &packet.common, Packet::Literal(ref packet) => &packet.common, Packet::CompressedData(ref packet) => &packet.common, - Packet::PKESK(ref packet) => &packet.common, + Packet::PKESK(PKESK::V3(packet)) => &packet.common, + Packet::PKESK(PKESK::V6(packet)) => &packet.common, Packet::SKESK(SKESK::V4(ref packet)) => &packet.common, Packet::SKESK(SKESK::V6(ref packet)) => &packet.skesk4.common, Packet::SEIP(SEIP::V1(packet)) => &packet.common, @@ -503,7 +507,8 @@ impl DerefMut for Packet { Packet::UserAttribute(ref mut packet) => &mut packet.common, Packet::Literal(ref mut packet) => &mut packet.common, Packet::CompressedData(ref mut packet) => &mut packet.common, - Packet::PKESK(ref mut packet) => &mut packet.common, + Packet::PKESK(PKESK::V3(packet)) => &mut packet.common, + Packet::PKESK(PKESK::V6(packet)) => &mut packet.common, Packet::SKESK(SKESK::V4(ref mut packet)) => &mut packet.common, Packet::SKESK(SKESK::V6(ref mut packet)) => &mut packet.skesk4.common, Packet::SEIP(SEIP::V1(packet)) => &mut packet.common, @@ -1186,6 +1191,8 @@ impl From for Packet { pub enum PKESK { /// PKESK packet version 3. V3(self::pkesk::PKESK3), + /// PKESK packet version 6. + V6(self::pkesk::PKESK6), } assert_send_and_sync!(PKESK); @@ -1194,34 +1201,73 @@ impl PKESK { pub fn version(&self) -> u8 { match self { PKESK::V3(_) => 3, + PKESK::V6(_) => 6, } } -} -impl From for Packet { - fn from(p: PKESK) -> Self { - Packet::PKESK(p) + /// Gets the recipient. + pub fn recipient(&self) -> Option { + match self { + PKESK::V3(p) => { + let id = p.recipient(); + if id.is_wildcard() { + None + } else { + Some(id.into()) + } + }, + PKESK::V6(p) => p.recipient().map(Into::into), + } } -} - -// Trivial forwarder for singleton enum. -impl Deref for PKESK { - type Target = self::pkesk::PKESK3; - fn deref(&self) -> &Self::Target { + /// Gets the public key algorithm. + pub fn pk_algo(&self) -> PublicKeyAlgorithm { match self { - PKESK::V3(ref p) => p, + PKESK::V3(p) => p.pk_algo(), + PKESK::V6(p) => p.pk_algo(), } } -} -// Trivial forwarder for singleton enum. -impl DerefMut for PKESK { - fn deref_mut(&mut self) -> &mut Self::Target { + /// Gets the encrypted session key. + pub fn esk(&self) -> &crate::crypto::mpi::Ciphertext { match self { - PKESK::V3(ref mut p) => p, + PKESK::V3(p) => p.esk(), + PKESK::V6(p) => p.esk(), } } + /// Decrypts the encrypted session key. + /// + /// If the symmetric algorithm used to encrypt the message is + /// known in advance, it should be given as argument. This allows + /// us to reduce the side-channel leakage of the decryption + /// operation for RSA. + /// + /// Returns the session key and symmetric algorithm used to + /// encrypt the following payload. + /// + /// Returns `None` on errors. This prevents leaking information + /// to an attacker, which could lead to compromise of secret key + /// material with certain algorithms (RSA). See [Section 14 of + /// RFC 4880]. + /// + /// [Section 14 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-14 + pub fn decrypt(&self, decryptor: &mut dyn Decryptor, + sym_algo_hint: Option) + -> Option<(Option, SessionKey)> + { + match self { + PKESK::V3(p) => p.decrypt(decryptor, sym_algo_hint) + .map(|(s, k)| (Some(s), k)), + PKESK::V6(p) => p.decrypt(decryptor, sym_algo_hint) + .map(|k| (None, k)), + } + } +} + +impl From for Packet { + fn from(p: PKESK) -> Self { + Packet::PKESK(p) + } } /// Holds a symmetrically encrypted session key. diff --git a/openpgp/src/packet/pkesk.rs b/openpgp/src/packet/pkesk.rs index c394e7c8..e4273bb8 100644 --- a/openpgp/src/packet/pkesk.rs +++ b/openpgp/src/packet/pkesk.rs @@ -21,6 +21,9 @@ use crate::SymmetricAlgorithm; use crate::crypto::SessionKey; use crate::packet; +mod v6; +pub use v6::PKESK6; + /// Holds an asymmetrically encrypted session key. /// /// The session key is needed to decrypt the actual ciphertext. See @@ -207,7 +210,11 @@ impl From for Packet { #[cfg(test)] impl Arbitrary for super::PKESK { fn arbitrary(g: &mut Gen) -> Self { - PKESK3::arbitrary(g).into() + if bool::arbitrary(g) { + PKESK3::arbitrary(g).into() + } else { + PKESK6::arbitrary(g).into() + } } } diff --git a/openpgp/src/packet/pkesk/v6.rs b/openpgp/src/packet/pkesk/v6.rs new file mode 100644 index 00000000..48ef38e8 --- /dev/null +++ b/openpgp/src/packet/pkesk/v6.rs @@ -0,0 +1,189 @@ +//! PublicKey-Encrypted Session Key packets version 6. +//! +//! The session key is needed to decrypt the actual ciphertext. See +//! [Section 5.1 of RFC 4880] for details. XXX fix reference +//! +//! [Section 5.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.1 + +#[cfg(test)] +use quickcheck::{Arbitrary, Gen}; + +use crate::packet::key; +use crate::packet::Key; +use crate::Fingerprint; +use crate::crypto::Decryptor; +use crate::crypto::mpi::Ciphertext; +use crate::Packet; +use crate::PublicKeyAlgorithm; +use crate::Result; +use crate::SymmetricAlgorithm; +use crate::crypto::SessionKey; +use crate::packet; + +/// Holds an asymmetrically encrypted session key. +/// +/// The session key is needed to decrypt the actual ciphertext. See +/// [Section 5.1 of RFC 4880] for details. XXX fix reference +/// +/// [Section 5.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.1 +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct PKESK6 { + /// CTB header fields. + pub(crate) common: packet::Common, + /// Fingerprint of the key this is encrypted to. + /// + /// If the value is `None`, the recipient has not been specified + /// by the sender to decrease metadata leakage. + recipient: Option, + /// Public key algorithm used to encrypt the session key. + pk_algo: PublicKeyAlgorithm, + /// The encrypted session key. + esk: Ciphertext, +} + +assert_send_and_sync!(PKESK6); + +impl PKESK6 { + /// Creates a new PKESK6 packet. + pub fn new(recipient: Option, pk_algo: PublicKeyAlgorithm, + encrypted_session_key: Ciphertext) + -> Result { + Ok(PKESK6 { + common: Default::default(), + recipient, + pk_algo, + esk: encrypted_session_key, + }) + } + + /// Creates a new PKESK6 packet for the given recipient. + /// + /// The given symmetric algorithm must match the algorithm that is + /// used to encrypt the payload. + pub fn for_recipient(session_key: &SessionKey, + recipient: &Key) + -> Result + where P: key::KeyParts, + R: key::KeyRole, + { + // XXX: Checksumming for non X25519 and X448 keys.. + let esk = recipient.encrypt(session_key)?; + Ok(PKESK6 { + common: Default::default(), + recipient: Some(recipient.fingerprint()), + pk_algo: recipient.pk_algo(), + esk, + }) + } + + /// Gets the recipient. + pub fn recipient(&self) -> Option<&Fingerprint> { + self.recipient.as_ref() + } + + /// Sets the recipient. + pub fn set_recipient(&mut self, recipient: Option) + -> Option { + std::mem::replace(&mut self.recipient, recipient) + } + + /// Gets the public key algorithm. + pub fn pk_algo(&self) -> PublicKeyAlgorithm { + self.pk_algo + } + + /// Sets the public key algorithm. + pub fn set_pk_algo(&mut self, algo: PublicKeyAlgorithm) + -> PublicKeyAlgorithm { + std::mem::replace(&mut self.pk_algo, algo) + } + + /// Gets the encrypted session key. + pub fn esk(&self) -> &Ciphertext { + &self.esk + } + + /// Sets the encrypted session key. + pub fn set_esk(&mut self, esk: Ciphertext) -> Ciphertext { + std::mem::replace(&mut self.esk, esk) + } + + /// Decrypts the encrypted session key. + /// + /// If the symmetric algorithm used to encrypt the message is + /// known in advance, it should be given as argument. This allows + /// us to reduce the side-channel leakage of the decryption + /// operation for RSA. + /// + /// Returns the session key and symmetric algorithm used to + /// encrypt the following payload. + /// + /// Returns `None` on errors. This prevents leaking information + /// to an attacker, which could lead to compromise of secret key + /// material with certain algorithms (RSA). See [Section 14 of + /// RFC 4880]. + /// + /// [Section 14 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-14 + pub fn decrypt(&self, decryptor: &mut dyn Decryptor, + sym_algo_hint: Option) + -> Option + { + self.decrypt_insecure(decryptor, sym_algo_hint).ok() + } + + fn decrypt_insecure(&self, decryptor: &mut dyn Decryptor, + sym_algo_hint: Option) + -> Result + { + // XXX: Checksumming for non X25519 and X448 keys.. + let plaintext_len = if let Some(s) = sym_algo_hint { + Some(s.key_size()? /* + 2 chksum */) + } else { + None + }; + let plain = decryptor.decrypt(&self.esk, plaintext_len)?; + Ok(plain) + } +} + +impl From for packet::PKESK { + fn from(p: PKESK6) -> Self { + packet::PKESK::V6(p) + } +} + +impl From for Packet { + fn from(p: PKESK6) -> Self { + Packet::PKESK(p.into()) + } +} + +#[cfg(test)] +impl Arbitrary for PKESK6 { + fn arbitrary(g: &mut Gen) -> Self { + let (ciphertext, pk_algo) = loop { + let ciphertext = Ciphertext::arbitrary(g); + if let Some(pk_algo) = ciphertext.pk_algo() { + break (ciphertext, pk_algo); + } + }; + + PKESK6::new(bool::arbitrary(g).then(|| Fingerprint::arbitrary_v6(g)), + pk_algo, ciphertext).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parse::Parse; + use crate::serialize::MarshalInto; + + quickcheck! { + fn roundtrip(p: PKESK6) -> bool { + let q = PKESK6::from_bytes(&p.to_vec().unwrap()).unwrap(); + assert_eq!(p, q); + true + } + } +} diff --git a/openpgp/src/packet/prelude.rs b/openpgp/src/packet/prelude.rs index bda7a7e2..0e9627d7 100644 --- a/openpgp/src/packet/prelude.rs +++ b/openpgp/src/packet/prelude.rs @@ -50,7 +50,10 @@ pub use crate::packet::{ key::SecretKeyMaterial, one_pass_sig::OnePassSig3, one_pass_sig::OnePassSig6, - pkesk::PKESK3, + pkesk::{ + PKESK3, + PKESK6, + }, seip::{ SEIP1, SEIP2, diff --git a/openpgp/src/packet/skesk.rs b/openpgp/src/packet/skesk.rs index 98075970..4be1a086 100644 --- a/openpgp/src/packet/skesk.rs +++ b/openpgp/src/packet/skesk.rs @@ -33,12 +33,13 @@ impl SKESK { /// tuple of the symmetric cipher to use with the key and the key /// itself. pub fn decrypt(&self, password: &Password) - -> Result<(SymmetricAlgorithm, SessionKey)> + -> Result<(Option, SessionKey)> { match self { - SKESK::V4(ref s) => s.decrypt(password), + SKESK::V4(s) => s.decrypt(password) + .map(|(algo, sk)| (Some(algo), sk)), SKESK::V6(ref s) => - Ok((SymmetricAlgorithm::Unencrypted, s.decrypt(password)?)), + Ok((None, s.decrypt(password)?)), } } } @@ -637,11 +638,11 @@ mod test { fn decrypt(&mut self, _: &[PKESK], skesks: &[SKESK], _: Option, mut decrypt: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { assert_eq!(skesks.len(), 1); let (cipher, sk) = skesks[0].decrypt(&"password".into())?; - assert_eq!(cipher, SymmetricAlgorithm::AES256); + assert_eq!(cipher, Some(SymmetricAlgorithm::AES256)); let r = decrypt(cipher, &sk); assert!(r); Ok(None) diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs index 894bfb89..84fa6913 100644 --- a/openpgp/src/parse.rs +++ b/openpgp/src/parse.rs @@ -3542,6 +3542,7 @@ impl PKESK { let version = php_try!(php.parse_u8("version")); match version { 3 => PKESK3::parse(php), + 6 => PKESK6::parse(php), _ => php.fail("unknown version"), } } @@ -3569,12 +3570,55 @@ impl PKESK3 { impl<'a> Parse<'a, PKESK3> for PKESK3 { fn from_reader(reader: R) -> Result { - PKESK::from_reader(reader).map(|p| match p { - PKESK::V3(p) => p, - // XXX: Once we have a second variant. - // - // p => Err(Error::InvalidOperation( - // format!("Not a PKESKv3 packet: {:?}", p)).into()), + PKESK::from_reader(reader).and_then(|p| match p { + PKESK::V3(p) => Ok(p), + p => Err(Error::InvalidOperation( + format!("Not a PKESKv3 packet: {:?}", p)).into()), + }) + } +} + +impl PKESK6 { + /// Parses the body of an PKESKv6 packet. + fn parse(mut php: PacketHeaderParser) -> Result { + make_php_try!(php); + let fp_len = php_try!(php.parse_u8("recipient_len")); + let fingerprint = if fp_len == 0 { + None + } else { + // Get the version and sanity check the length. + let fp_version = php_try!(php.parse_u8("recipient_version")); + if let Some(expected_length) = match fp_version { + 4 => Some(20), + 6 => Some(32), + _ => None, + } { + if fp_len - 1 != expected_length { + return php.fail("bad fingerprint length"); + } + } + Some(Fingerprint::from_bytes( + &php_try!(php.parse_bytes("recipient", (fp_len - 1).into())))) + }; + + let pk_algo: PublicKeyAlgorithm = + php_try!(php.parse_u8("pk_algo")).into(); + if ! pk_algo.for_encryption() { // XXX + return php.fail("not an encryption algorithm"); + } + let mpis = crypto::mpi::Ciphertext::_parse(pk_algo, &mut php)?; + + let pkesk = php_try!(PKESK6::new(fingerprint, pk_algo, mpis)); + php.ok(pkesk.into()) + } +} + +impl<'a> Parse<'a, PKESK6> for PKESK6 { + fn from_reader(reader: R) -> Result { + PKESK::from_reader(reader).and_then(|p| match p { + PKESK::V6(p) => Ok(p), + p => Err(Error::InvalidOperation( + format!("Not a PKESKv6 packet: {:?}", p)).into()), }) } } @@ -5840,8 +5884,18 @@ impl<'a> PacketParser<'a> { /// OpenPGP]. /// /// [Format Oracles on OpenPGP]: https://www.ssi.gouv.fr/uploads/2015/05/format-Oracles-on-OpenPGP.pdf - pub fn decrypt(&mut self, algo: SymmetricAlgorithm, key: &SessionKey) - -> Result<()> + pub fn decrypt(&mut self, algo: A, key: &SessionKey) + -> Result<()> + where + A: Into>, + { + self.decrypt_(algo.into(), key) + } + + fn decrypt_(&mut self, + algo: Option, + key: &SessionKey) + -> Result<()> { let indent = self.recursion_depth(); tracer!(TRACE, "PacketParser::decrypt", indent); @@ -5855,14 +5909,23 @@ impl<'a> PacketParser<'a> { "Packet not encrypted.".to_string()).into()); } - if algo.key_size()? != key.len () { - return Err(Error::InvalidOperation( - format!("Bad key size: {} expected: {}", - key.len(), algo.key_size()?)).into()); - } - match self.packet.clone() { Packet::SEIP(SEIP::V1(_)) => { + let algo = if let Some(a) = algo { + a + } else { + return Err(Error::InvalidOperation( + "Trying to decrypt a SEIPDv1 packet: \ + no symmetric algorithm given".into()).into()); + }; + + if algo.key_size()? != key.len () { + return Err(Error::InvalidOperation( + format!("Bad key size: {} expected: {}", + key.len(), algo.key_size()?)).into()); + } + + // Get the first blocksize plus two bytes and check // whether we can decrypt them using the provided key. // Don't actually consume them in case we can't. @@ -6313,7 +6376,7 @@ mod test { let key = crate::fmt::from_hex(test.key_hex, false) .unwrap().into(); - pp.decrypt(test.algo, &key).unwrap(); + pp.decrypt(Some(test.algo), &key).unwrap(); } else { panic!("Expected a SEIP/AED packet. Got: {:?}", ppr); } @@ -6410,7 +6473,7 @@ mod test { Packet::SEIP(_) | Packet::AED(_) => { let key = crate::fmt::from_hex(test.key_hex, false) .unwrap().into(); - pp.decrypt(test.algo, &key).unwrap(); + pp.decrypt(Some(test.algo), &key).unwrap(); }, Packet::Literal(_) => { assert!(! saw_literal); diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index 9641ed4f..eeae2d84 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -930,7 +930,7 @@ impl DecryptionHelper for NoDecryptionHelper { fn decrypt(&mut self, _: &[PKESK], _: &[SKESK], _: Option, _: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { unreachable!("This is not used for verifications") } @@ -1718,7 +1718,7 @@ enum Mode { /// fn decrypt(&mut self, _: &[PKESK], skesks: &[SKESK], /// _sym_algo: Option, /// mut decrypt: D) -> Result> -/// where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool +/// where D: FnMut(Option, &SessionKey) -> bool /// { /// skesks[0].decrypt(&"streng geheim".into()) /// .map(|(algo, session_key)| decrypt(algo, &session_key)); @@ -1882,7 +1882,7 @@ impl<'a> DecryptorBuilder<'a> { /// # fn decrypt(&mut self, _: &[PKESK], skesks: &[SKESK], /// # _sym_algo: Option, /// # mut decrypt: D) -> Result> - /// # where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + /// # where D: FnMut(Option, &SessionKey) -> bool /// # { /// # Ok(None) /// # } @@ -1949,7 +1949,7 @@ impl<'a> DecryptorBuilder<'a> { /// # fn decrypt(&mut self, _: &[PKESK], skesks: &[SKESK], /// # _sym_algo: Option, /// # mut decrypt: D) -> Result> - /// # where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + /// # where D: FnMut(Option, &SessionKey) -> bool /// # { /// # Ok(None) /// # } @@ -2014,7 +2014,7 @@ impl<'a> DecryptorBuilder<'a> { /// # fn decrypt(&mut self, _: &[PKESK], skesks: &[SKESK], /// # _sym_algo: Option, /// # mut decrypt: D) -> Result> - /// # where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + /// # where D: FnMut(Option, &SessionKey) -> bool /// # { /// # Ok(None) /// # } @@ -2098,18 +2098,17 @@ pub trait DecryptionHelper { /// /// ``` /// use sequoia_openpgp as openpgp; - /// use openpgp::{Fingerprint, Cert, Result}; - /// # use openpgp::KeyID; + /// use openpgp::{Cert, Fingerprint, KeyHandle, KeyID, Result}; /// use openpgp::crypto::SessionKey; /// use openpgp::types::SymmetricAlgorithm; /// use openpgp::packet::{PKESK, SKESK}; /// # use openpgp::packet::{Key, key::*}; /// use openpgp::parse::stream::*; /// # fn lookup_cache(_: &[PKESK], _: &[SKESK]) - /// # -> Option<(Option, SymmetricAlgorithm, SessionKey)> { + /// # -> Option<(Option, Option, SessionKey)> { /// # unimplemented!() /// # } - /// # fn lookup_key(_: &KeyID) + /// # fn lookup_key(_: Option) /// # -> Option<(Fingerprint, Key)> { /// # unimplemented!() /// # } @@ -2122,7 +2121,7 @@ pub trait DecryptionHelper { /// fn decrypt(&mut self, pkesks: &[PKESK], skesks: &[SKESK], /// sym_algo: Option, /// mut decrypt: D) -> Result> - /// where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + /// where D: FnMut(Option, &SessionKey) -> bool /// { /// // Try to decrypt, from the most convenient method to the /// // least convenient one. @@ -2153,7 +2152,9 @@ pub trait DecryptionHelper { /// // Third, we try to decrypt PKESK packets with /// // wildcard recipients using those keys that we can /// // use without prompting for a password. - /// for pkesk in pkesks.iter().filter(|p| p.recipient().is_wildcard()) { + /// for pkesk in pkesks.iter().filter( + /// |p| p.recipient().is_none()) + /// { /// for (fp, key) in all_keys() { /// if ! key.secret().is_encrypted() { /// let mut keypair = key.clone().into_keypair()?; @@ -2204,7 +2205,7 @@ pub trait DecryptionHelper { fn decrypt(&mut self, pkesks: &[PKESK], skesks: &[SKESK], sym_algo: Option, decrypt: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool; + where D: FnMut(Option, &SessionKey) -> bool; } impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { @@ -2427,7 +2428,7 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { let decryption_proxy = |algo, secret: &SessionKey| { // Take the algo from the AED packet over // the dummy one from the SKESK6 packet. - let algo = sym_algo_hint.unwrap_or(algo); + let algo = sym_algo_hint.or(algo); let result = pp.decrypt(algo, secret); t!("pp.decrypt({:?}, {:?}) => {:?}", algo, secret, result); @@ -2450,8 +2451,13 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { "No session key decrypted".into()).into()); } - let sym_algo = - sym_algo.expect("if we got here, sym_algo is set"); + let sym_algo = if let Some(Some(a)) = sym_algo { + a + } else { + return Err(Error::InvalidOperation( + "No symmetric algorithm known".into()).into()); + }; + v.policy.symmetric_algorithm(sym_algo)?; if let Packet::AED(ref p) = pp.packet { v.policy.aead_algorithm(p.aead())?; @@ -3125,7 +3131,7 @@ pub mod test { fn decrypt(&mut self, pkesks: &[PKESK], skesks: &[SKESK], sym_algo: Option, mut decrypt: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { let p = P::new(); if ! self.for_decryption { @@ -3142,10 +3148,10 @@ pub mod test { } } - for pkesk in pkesks { + for pkesk in pkesks.iter().filter(|p| p.recipient().is_some()) { for key in &self.keys { for subkey in key.with_policy(&p, None)?.keys().secret() - .key_handle(pkesk.recipient()) + .key_handle(pkesk.recipient().unwrap()) { if let Some((algo, sk)) = subkey.key().clone().into_keypair().ok() @@ -3498,7 +3504,7 @@ pub mod test { fn decrypt(&mut self, _: &[PKESK], _: &[SKESK], _: Option, _: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { unreachable!(); } @@ -3756,7 +3762,7 @@ pub mod test { fn decrypt(&mut self, _: &[PKESK], s: &[SKESK], _: Option, mut decrypt: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { let (algo, sk) = s[0].decrypt(&"123".into()).unwrap(); let r = decrypt(algo, &sk); diff --git a/openpgp/src/policy.rs b/openpgp/src/policy.rs index 5f4d2ea4..a484beaf 100644 --- a/openpgp/src/policy.rs +++ b/openpgp/src/policy.rs @@ -2207,7 +2207,7 @@ mod test { fn decrypt(&mut self, _: &[PKESK], _: &[SKESK], _: Option,_: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { unreachable!(); } @@ -2713,7 +2713,7 @@ mod test { fn decrypt(&mut self, _: &[PKESK], _: &[SKESK], _: Option,_: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { unreachable!(); } @@ -2833,7 +2833,7 @@ mod test { fn decrypt(&mut self, _: &[PKESK], _: &[SKESK], _: Option, _: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool { + where D: FnMut(Option, &SessionKey) -> bool { Ok(None) } } @@ -2880,7 +2880,7 @@ mod test { fn decrypt(&mut self, pkesks: &[PKESK], _: &[SKESK], algo: Option, mut decrypt: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { let p = &P::new(); let mut pair = Cert::from_bytes( diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs index 846a3c72..e555183e 100644 --- a/openpgp/src/serialize.rs +++ b/openpgp/src/serialize.rs @@ -2720,6 +2720,16 @@ impl Marshal for PKESK { fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { match self { PKESK::V3(ref p) => p.serialize(o), + PKESK::V6(p) => p.serialize(o), + } + } +} + +impl NetLength for PKESK { + fn net_len(&self) -> usize { + match self { + PKESK::V3(p) => p.net_len(), + PKESK::V6(p) => p.net_len(), } } } @@ -2728,6 +2738,7 @@ impl MarshalInto for PKESK { fn serialized_len(&self) -> usize { match self { PKESK::V3(ref p) => p.serialized_len(), + PKESK::V6(p) => p.serialized_len(), } } @@ -2735,6 +2746,8 @@ impl MarshalInto for PKESK { match self { PKESK::V3(p) => generic_serialize_into(p, MarshalInto::serialized_len(p), buf), + PKESK::V6(p) => + generic_serialize_into(p, MarshalInto::serialized_len(p), buf), } } } @@ -2770,6 +2783,52 @@ impl MarshalInto for PKESK3 { } } +impl seal::Sealed for PKESK6 {} +impl Marshal for PKESK6 { + fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { + write_byte(o, 6)?; // Version. + if let Some(recipient) = self.recipient() { + // Recipient length. + write_byte(o, ((recipient as &dyn MarshalInto).serialized_len() + 1) as u8)?; + match recipient { + Fingerprint::V4(_) => write_byte(o, 4)?, + Fingerprint::V6(_) => write_byte(o, 6)?, + Fingerprint::Invalid(_) => write_byte(o, 0xff)?, + } + (recipient as &dyn Marshal).serialize(o)?; + } else { + // No recipient. + write_byte(o, 0)?; + } + + write_byte(o, self.pk_algo().into())?; + self.esk().serialize(o)?; + + Ok(()) + } +} + +impl NetLength for PKESK6 { + fn net_len(&self) -> usize { + 1 // Version. + + 1 // Recipient length. + // Recipient's versioned fingerprint, if any: + + self.recipient().map(|r| r.as_bytes().len() + 1).unwrap_or(0) + + 1 // Algo. + + self.esk().serialized_len() + } +} + +impl MarshalInto for PKESK6 { + fn serialized_len(&self) -> usize { + self.net_len() + } + + fn serialize_into(&self, buf: &mut [u8]) -> Result { + generic_serialize_into(self, MarshalInto::serialized_len(self), buf) + } +} + impl seal::Sealed for SKESK {} impl Marshal for SKESK { fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs index 93b6ddd9..d147f979 100644 --- a/openpgp/src/serialize/stream.rs +++ b/openpgp/src/serialize/stream.rs @@ -2508,7 +2508,8 @@ impl<'a> Encryptor<'a> { /// for p in pkesks { // Emit the stashed PKESK packets. /// Packet::from(p).serialize(&mut message)?; /// } - /// let message = Encryptor::with_session_key(message, algo, sk)?.build()?; + /// let message = Encryptor::with_session_key( + /// message, algo.unwrap_or_default(), sk)?.build()?; /// let mut w = LiteralWriter::new(message).build()?; /// w.write_all(b"Encrypted reply")?; /// w.finalize()?; @@ -2521,7 +2522,7 @@ impl<'a> Encryptor<'a> { /// /// Decrypts the message preserving algo, session key, and PKESKs. /// struct Helper { /// key: Cert, - /// recycling_bin: Option<(SymmetricAlgorithm, SessionKey, Vec)>, + /// recycling_bin: Option<(Option, SessionKey, Vec)>, /// } /// /// # impl Helper { @@ -2534,7 +2535,7 @@ impl<'a> Encryptor<'a> { /// fn decrypt(&mut self, pkesks: &[PKESK], _skesks: &[SKESK], /// sym_algo: Option, mut decrypt: D) /// -> Result> - /// where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + /// where D: FnMut(Option, &SessionKey) -> bool /// { /// let p = &StandardPolicy::new(); /// let mut encryption_context = None; @@ -2542,7 +2543,7 @@ impl<'a> Encryptor<'a> { /// for pkesk in pkesks { // Try each PKESK until we succeed. /// for ka in self.key.keys().with_policy(p, None) /// .supported().unencrypted_secret() - /// .key_handle(pkesk.recipient()) + /// .key_handles2(pkesk.recipient()) /// .for_storage_encryption().for_transport_encryption() /// { /// let mut pair = ka.key().clone().into_keypair().unwrap(); @@ -2882,6 +2883,13 @@ impl<'a> Encryptor<'a> { ).into()); } + // XXX autodetect v6 + if ! self.recipients.is_empty() + && self.recipients.iter().all(|r| r.key.version() == 6) + { + self.aead_algo = Some(AEADAlgorithm::const_default()); + } + struct AEADParameters { algo: AEADAlgorithm, chunk_size: usize, @@ -2915,10 +2923,17 @@ impl<'a> Encryptor<'a> { // Write the PKESK packet(s). for recipient in self.recipients.iter() { - let mut pkesk = - PKESK3::for_recipient(self.sym_algo, &sk, recipient.key)?; - pkesk.set_recipient(recipient.keyid.clone()); - Packet::PKESK(pkesk.into()).serialize(&mut inner)?; + if aead.is_some() { + let pkesk = + PKESK6::for_recipient(&sk, recipient.key)?; + // XXX: handle anonymous recipient/ different recipient fps + Packet::PKESK(pkesk.into()).serialize(&mut inner)?; + } else { + let mut pkesk = + PKESK3::for_recipient(self.sym_algo, &sk, recipient.key)?; + pkesk.set_recipient(recipient.keyid.clone()); + Packet::PKESK(pkesk.into()).serialize(&mut inner)?; + } } // Write the SKESK packet(s). @@ -3320,7 +3335,7 @@ mod test { #[derive(Debug, PartialEq)] enum State { Start, - Decrypted(Vec<(SymmetricAlgorithm, SessionKey)>), + Decrypted(Vec<(Option, SessionKey)>), Deciphered, MDC, Done, @@ -3483,7 +3498,7 @@ mod test { fn decrypt(&mut self, pkesks: &[PKESK], _skesks: &[SKESK], sym_algo: Option, mut decrypt: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { let mut keypair = self.tsk.keys().with_policy(self.policy, None) .for_transport_encryption() @@ -3914,7 +3929,7 @@ mod test { fn decrypt(&mut self, _: &[PKESK], skesks: &[SKESK], _sym_algo: Option, mut decrypt: D) -> Result> - where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool + where D: FnMut(Option, &SessionKey) -> bool { skesks[0].decrypt(&"совершенно секретно".into()) .map(|(algo, session_key)| decrypt(algo, &session_key))?; @@ -4012,6 +4027,9 @@ mod test { "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1", "secp256k1", ].iter().map(|alg| format!("messages/encrypted/{}.sec.pgp", alg)) + .chain(vec![ + "crypto-refresh/v6-minimal-secret.key".into(), + ].into_iter()) { eprintln!("Test vector {:?}...", path); let key = Cert::from_bytes(crate::tests::file(&path))?; -- cgit v1.2.3