From cf923f1ea07493c10dda6ecf21009f699eacd432 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 16 Feb 2023 16:13:04 +0100 Subject: openpgp: Implement v2 SEIPD. --- openpgp/src/crypto/aead.rs | 82 ++++++++++++++++++ openpgp/src/message/grammar.lalrpop | 6 ++ openpgp/src/message/lexer.rs | 2 + openpgp/src/message/mod.rs | 1 + openpgp/src/packet/container.rs | 11 ++- openpgp/src/packet/mod.rs | 30 ++----- openpgp/src/packet/prelude.rs | 5 +- openpgp/src/packet/seip.rs | 3 + openpgp/src/packet/seip/v2.rs | 166 ++++++++++++++++++++++++++++++++++++ openpgp/src/parse.rs | 104 ++++++++++++++++++++-- openpgp/src/parse/stream.rs | 74 +++++++++++++++- openpgp/src/serialize.rs | 95 ++++++++++++++++++++- openpgp/src/serialize/stream.rs | 30 ++++--- 13 files changed, 556 insertions(+), 53 deletions(-) create mode 100644 openpgp/src/packet/seip/v2.rs diff --git a/openpgp/src/crypto/aead.rs b/openpgp/src/crypto/aead.rs index 4cf0cfd1..706abb07 100644 --- a/openpgp/src/crypto/aead.rs +++ b/openpgp/src/crypto/aead.rs @@ -17,6 +17,7 @@ use crate::Result; use crate::crypto::SessionKey; use crate::seal; use crate::parse::Cookie; +use crate::crypto::backend::{Backend, interface::Kdf}; /// Minimum AEAD chunk size. /// @@ -232,6 +233,87 @@ impl Schedule for AEDv1Schedule { } } +const SEIP2AD_PREFIX_LEN: usize = 5; +pub(crate) struct SEIPv2Schedule { + nonce: Box<[u8]>, + ad: [u8; SEIP2AD_PREFIX_LEN], + nonce_len: usize, +} + +impl SEIPv2Schedule { + pub(crate) fn new(session_key: &SessionKey, + sym_algo: SymmetricAlgorithm, + aead: AEADAlgorithm, + chunk_size: usize, + salt: &[u8]) -> Result<(SessionKey, Self)> + { + if !(MIN_CHUNK_SIZE..=MAX_CHUNK_SIZE).contains(&chunk_size) { + return Err(Error::InvalidArgument( + format!("Invalid AEAD chunk size: {}", chunk_size)).into()); + } + + // Derive the message key and initialization vector. + let key_size = sym_algo.key_size()?; + // The NONCE size is NONCE_LEN - 8 bytes taken from the KDF. + let nonce_size = aead.nonce_size()? - 8; + let mut key_nonce: SessionKey = + vec![0; key_size + nonce_size].into(); + let ad = [ + 0xd2, // Tag. + 2, // Version. + sym_algo.into(), + aead.into(), + chunk_size.trailing_zeros() as u8 - 6, + ]; + Backend::hkdf_sha256(session_key, Some(salt), &ad, &mut key_nonce)?; + let key = Vec::from(&key_nonce[..key_size]).into(); + let nonce = Vec::from(&key_nonce[key_size..]).into(); + + Ok((key, Self { + nonce, + ad, + nonce_len: aead.nonce_size()?, + })) + } +} + +impl Schedule for SEIPv2Schedule { + fn next_chunk(&self, index: u64, mut fun: F) -> R + where + F: FnMut(&[u8], &[u8]) -> R, + { + // The nonce is the NONCE (NONCE_LEN - 8 bytes taken from the + // KDF) concatenated with the chunk index. + let index_be: [u8; 8] = index.to_be_bytes(); + let mut nonce_store = [0u8; MAX_NONCE_LEN]; + let nonce = &mut nonce_store[..self.nonce_len]; + nonce[..self.nonce.len()].copy_from_slice(&self.nonce); + nonce[self.nonce.len()..].copy_from_slice(&index_be); + + fun(nonce, &self.ad) + } + + fn final_chunk(&self, index: u64, length: u64, mut fun: F) -> R + where + F: FnMut(&[u8], &[u8]) -> R, + { + // Prepare the associated data. + let mut ad = [0u8; SEIP2AD_PREFIX_LEN + 8]; + ad[..SEIP2AD_PREFIX_LEN].copy_from_slice(&self.ad); + write_be_u64(&mut ad[SEIP2AD_PREFIX_LEN..], length); + + // The nonce is the NONCE (NONCE_LEN - 8 bytes taken from the + // KDF) concatenated with the chunk index. + let index_be: [u8; 8] = index.to_be_bytes(); + let mut nonce_store = [0u8; MAX_NONCE_LEN]; + let nonce = &mut nonce_store[..self.nonce_len]; + nonce[..self.nonce.len()].copy_from_slice(&self.nonce); + nonce[self.nonce.len()..].copy_from_slice(&index_be); + + fun(nonce, &ad) + } +} + /// A `Read`er for decrypting AEAD-encrypted data. pub struct Decryptor<'a, S: Schedule> { // The encrypted data. diff --git a/openpgp/src/message/grammar.lalrpop b/openpgp/src/message/grammar.lalrpop index f98b3786..03b5776f 100644 --- a/openpgp/src/message/grammar.lalrpop +++ b/openpgp/src/message/grammar.lalrpop @@ -21,6 +21,10 @@ Seipv1Part: () = { SEIPv1 OPAQUE_CONTENT POP, } +Seipv2Part: () = { + SEIPv2 Message POP, +} + AedPart: () = { AED Message POP, } @@ -33,6 +37,7 @@ EncryptedPart: () = { EncryptionContainer: () = { Seipv1Part, + Seipv2Part, AedPart, }; @@ -69,6 +74,7 @@ extern { SKESK => lexer::Token::SKESK, PKESK => lexer::Token::PKESK, SEIPv1 => lexer::Token::SEIPv1, + SEIPv2 => lexer::Token::SEIPv2, MDC => lexer::Token::MDC, AED => lexer::Token::AED, OPS => lexer::Token::OPS, diff --git a/openpgp/src/message/lexer.rs b/openpgp/src/message/lexer.rs index 7289d748..e938c4e0 100644 --- a/openpgp/src/message/lexer.rs +++ b/openpgp/src/message/lexer.rs @@ -27,6 +27,8 @@ pub enum Token { PKESK, /// A version 1 SEIP packet. SEIPv1, + /// A version 2 SEIP packet. + SEIPv2, /// An MDC packet. MDC, /// An AED packet. diff --git a/openpgp/src/message/mod.rs b/openpgp/src/message/mod.rs index 2d2d6884..ec630736 100644 --- a/openpgp/src/message/mod.rs +++ b/openpgp/src/message/mod.rs @@ -207,6 +207,7 @@ impl MessageValidator { Tag::SKESK => Token::SKESK, Tag::PKESK => Token::PKESK, Tag::SEIP if version == Some(1) => Token::SEIPv1, + Tag::SEIP if version == Some(2) => Token::SEIPv2, Tag::MDC => Token::MDC, Tag::AED => Token::AED, Tag::OnePassSig => Token::OPS, diff --git a/openpgp/src/packet/container.rs b/openpgp/src/packet/container.rs index fd8c0f2f..7bfc20ba 100644 --- a/openpgp/src/packet/container.rs +++ b/openpgp/src/packet/container.rs @@ -12,7 +12,10 @@ use xxhash_rust::xxh3::Xxh3; use crate::{ Packet, - packet::Iter, + packet::{ + Iter, + SEIP, + }, }; /// A packet's body holds either unprocessed bytes, processed bytes, @@ -400,7 +403,8 @@ impl Packet { use std::ops::Deref; match self { Packet::CompressedData(p) => Some(p.deref()), - Packet::SEIP(p) => Some(p.deref()), + Packet::SEIP(SEIP::V1(p)) => Some(p.deref()), + Packet::SEIP(SEIP::V2(p)) => Some(p.deref()), Packet::AED(p) => Some(p.deref()), Packet::Literal(p) => Some(p.container_ref()), Packet::Unknown(p) => Some(p.container_ref()), @@ -413,7 +417,8 @@ impl Packet { use std::ops::DerefMut; match self { Packet::CompressedData(p) => Some(p.deref_mut()), - Packet::SEIP(p) => Some(p.deref_mut()), + Packet::SEIP(SEIP::V1(p)) => Some(p.deref_mut()), + Packet::SEIP(SEIP::V2(p)) => Some(p.deref_mut()), Packet::AED(p) => Some(p.deref_mut()), Packet::Literal(p) => Some(p.container_mut()), Packet::Unknown(p) => Some(p.container_mut()), diff --git a/openpgp/src/packet/mod.rs b/openpgp/src/packet/mod.rs index 6956791c..81eb23b6 100644 --- a/openpgp/src/packet/mod.rs +++ b/openpgp/src/packet/mod.rs @@ -481,7 +481,8 @@ impl Deref for Packet { Packet::PKESK(ref packet) => &packet.common, Packet::SKESK(SKESK::V4(ref packet)) => &packet.common, Packet::SKESK(SKESK::V6(ref packet)) => &packet.skesk4.common, - Packet::SEIP(ref packet) => &packet.common, + Packet::SEIP(SEIP::V1(packet)) => &packet.common, + Packet::SEIP(SEIP::V2(packet)) => &packet.common, #[allow(deprecated)] Packet::MDC(ref packet) => &packet.common, Packet::AED(ref packet) => &packet.common, @@ -510,7 +511,8 @@ impl DerefMut for Packet { Packet::PKESK(ref mut 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(ref mut packet) => &mut packet.common, + Packet::SEIP(SEIP::V1(packet)) => &mut packet.common, + Packet::SEIP(SEIP::V2(packet)) => &mut packet.common, #[allow(deprecated)] Packet::MDC(ref mut packet) => &mut packet.common, Packet::AED(ref mut packet) => &mut packet.common, @@ -2116,6 +2118,9 @@ impl DerefMut for Key { pub enum SEIP { /// SEIP packet version 1. V1(self::seip::SEIP1), + + /// SEIP packet version 2. + V2(self::seip::SEIP2), } assert_send_and_sync!(SEIP); @@ -2124,6 +2129,7 @@ impl SEIP { pub fn version(&self) -> u8 { match self { SEIP::V1(_) => 1, + SEIP::V2(_) => 2, } } } @@ -2134,26 +2140,6 @@ impl From for Packet { } } -// Trivial forwarder for singleton enum. -impl Deref for SEIP { - type Target = self::seip::SEIP1; - - fn deref(&self) -> &Self::Target { - match self { - SEIP::V1(ref p) => p, - } - } -} - -// Trivial forwarder for singleton enum. -impl DerefMut for SEIP { - fn deref_mut(&mut self) -> &mut Self::Target { - match self { - SEIP::V1(ref mut p) => p, - } - } -} - /// Holds an AEAD encrypted data packet. /// /// An AEAD packet holds encrypted data. It is contains additional diff --git a/openpgp/src/packet/prelude.rs b/openpgp/src/packet/prelude.rs index c06339c1..bda7a7e2 100644 --- a/openpgp/src/packet/prelude.rs +++ b/openpgp/src/packet/prelude.rs @@ -51,7 +51,10 @@ pub use crate::packet::{ one_pass_sig::OnePassSig3, one_pass_sig::OnePassSig6, pkesk::PKESK3, - seip::SEIP1, + seip::{ + SEIP1, + SEIP2, + }, signature, signature::Signature4, signature::Signature6, diff --git a/openpgp/src/packet/seip.rs b/openpgp/src/packet/seip.rs index b808acf2..e74db749 100644 --- a/openpgp/src/packet/seip.rs +++ b/openpgp/src/packet/seip.rs @@ -8,6 +8,9 @@ use crate::packet; use crate::Packet; +mod v2; +pub use v2::*; + /// Holds an encrypted data packet. /// /// An encrypted data packet is a container. See [Section 5.13 of RFC diff --git a/openpgp/src/packet/seip/v2.rs b/openpgp/src/packet/seip/v2.rs new file mode 100644 index 00000000..9942e02a --- /dev/null +++ b/openpgp/src/packet/seip/v2.rs @@ -0,0 +1,166 @@ +//! Symmetrically Encrypted Integrity Protected data packets version 2. +//! +//! An encrypted data packet is a container. See [Section 5.13.2 of +//! draft-ietf-openpgp-crypto-refresh-10] for details. +//! +//! [Section 5.13.2 of draft-ietf-openpgp-crypto-refresh-10]: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-version-2-symmetrically-enc +use crate::{ + Error, + packet::{ + self, + Packet, + SEIP, + }, + Result, + types::{ + AEADAlgorithm, + SymmetricAlgorithm, + }, +}; + +/// Holds an encrypted data packet. +/// +/// An encrypted data packet is a container. See [Section 5.13.2 of +/// draft-ietf-openpgp-crypto-refresh-10] for details. +/// +/// [Section 5.13.2 of draft-ietf-openpgp-crypto-refresh-10]: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#name-version-2-symmetrically-enc +/// +/// # A note on equality +/// +/// An unprocessed (encrypted) `SEIP2` packet is never considered equal +/// to a processed (decrypted) one. Likewise, a processed (decrypted) +/// packet is never considered equal to a structured (parsed) one. +// IMPORTANT: If you add fields to this struct, you need to explicitly +// IMPORTANT: implement PartialEq, Eq, and Hash. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct SEIP2 { + /// CTB packet header fields. + pub(crate) common: packet::Common, + + /// Symmetric algorithm. + sym_algo: SymmetricAlgorithm, + /// AEAD algorithm. + aead: AEADAlgorithm, + /// Chunk size. + chunk_size: u64, + /// Salt. + salt: [u8; 32], + + /// This is a container packet. + container: packet::Container, +} + +assert_send_and_sync!(SEIP2); + +impl std::ops::Deref for SEIP2 { + type Target = packet::Container; + fn deref(&self) -> &Self::Target { + &self.container + } +} + +impl std::ops::DerefMut for SEIP2 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.container + } +} + +impl SEIP2 { + /// Creates a new SEIP2 packet. + pub fn new(sym_algo: SymmetricAlgorithm, + aead: AEADAlgorithm, + chunk_size: u64, + salt: [u8; 32]) -> Result { + if chunk_size.count_ones() != 1 { + return Err(Error::InvalidArgument( + format!("chunk size is not a power of two: {}", chunk_size)) + .into()); + } + + if chunk_size < 64 { + return Err(Error::InvalidArgument( + format!("chunk size is too small: {}", chunk_size)) + .into()); + } + + Ok(SEIP2 { + common: Default::default(), + sym_algo, + aead, + chunk_size, + salt, + container: Default::default(), + }) + } + + /// Gets the symmetric algorithm. + pub fn symmetric_algo(&self) -> SymmetricAlgorithm { + self.sym_algo + } + + /// Sets the symmetric algorithm. + pub fn set_symmetric_algo(&mut self, sym_algo: SymmetricAlgorithm) + -> SymmetricAlgorithm { + std::mem::replace(&mut self.sym_algo, sym_algo) + } + + /// Gets the AEAD algorithm. + pub fn aead(&self) -> AEADAlgorithm { + self.aead + } + + /// Sets the AEAD algorithm. + pub fn set_aead(&mut self, aead: AEADAlgorithm) -> AEADAlgorithm { + std::mem::replace(&mut self.aead, aead) + } + + /// Gets the chunk size. + pub fn chunk_size(&self) -> u64 { + self.chunk_size + } + + /// Sets the chunk size. + pub fn set_chunk_size(&mut self, chunk_size: u64) -> Result<()> { + if chunk_size.count_ones() != 1 { + return Err(Error::InvalidArgument( + format!("chunk size is not a power of two: {}", chunk_size)) + .into()); + } + + if chunk_size < 64 { + return Err(Error::InvalidArgument( + format!("chunk size is too small: {}", chunk_size)) + .into()); + } + + self.chunk_size = chunk_size; + Ok(()) + } + + /// Gets the size of a chunk with a digest. + pub fn chunk_digest_size(&self) -> Result { + Ok(self.chunk_size + self.aead.digest_size()? as u64) + } + + /// Gets the salt. + pub fn salt(&self) -> &[u8; 32] { + &self.salt + } + + /// Sets the salt. + pub fn set_salt(&mut self, salt: [u8; 32]) -> [u8; 32] { + std::mem::replace(&mut self.salt, salt) + } +} + +impl From for SEIP { + fn from(p: SEIP2) -> Self { + SEIP::V2(p) + } +} + +impl From for Packet { + fn from(s: SEIP2) -> Self { + Packet::SEIP(s.into()) + } +} diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs index 935d2412..b5dc983f 100644 --- a/openpgp/src/parse.rs +++ b/openpgp/src/parse.rs @@ -3447,16 +3447,49 @@ impl SEIP { fn parse(mut php: PacketHeaderParser) -> Result { make_php_try!(php); let version = php_try!(php.parse_u8("version")); - if version != 1 { - return php.fail("unknown version"); + match version { + 1 => SEIP1::parse(php), + 2 => SEIP2::parse(php), + _ => php.fail("unknown version"), } + } +} +impl_parse_with_buffered_reader!(SEIP); + +impl SEIP1 { + /// Parses the body of a SEIP1 packet. + fn parse(php: PacketHeaderParser) -> Result { php.ok(SEIP1::new().into()) .map(|pp| pp.set_processed(false)) } } -impl_parse_with_buffered_reader!(SEIP); +impl SEIP2 { + /// Parses the body of a SEIP1 packet. + fn parse(mut php: PacketHeaderParser) -> Result { + make_php_try!(php); + let cipher: SymmetricAlgorithm = + php_try!(php.parse_u8("sym_algo")).into(); + let aead: AEADAlgorithm = + php_try!(php.parse_u8("aead_algo")).into(); + let chunk_size = php_try!(php.parse_u8("chunk_size")); + + // An implementation MUST accept chunk size octets with values + // from 0 to 16. An implementation MUST NOT create data with a + // chunk size octet value larger than 16 (4 MiB chunks). + if chunk_size > 16 { + return php.fail("unsupported chunk size"); + } + let chunk_size: u64 = 1 << (chunk_size + 6); + let salt_v = php_try!(php.parse_bytes("salt", 32)); + let mut salt = [0u8; 32]; + salt.copy_from_slice(&salt_v); + + let seip2 = php_try!(Self::new(cipher, aead, chunk_size, salt)); + php.ok(seip2.into()).map(|pp| pp.set_processed(false)) + } +} impl MDC { /// Parses the body of an MDC packet. @@ -5569,7 +5602,9 @@ impl <'a> PacketParser<'a> { Packet::Unknown(p) => set_or_extend(rest, p.container_mut(), false), Packet::CompressedData(p) => set_or_extend(rest, p.deref_mut(), self.processed), - Packet::SEIP(p) => + Packet::SEIP(SEIP::V1(p)) => + set_or_extend(rest, p.deref_mut(), self.processed), + Packet::SEIP(SEIP::V2(p)) => set_or_extend(rest, p.deref_mut(), self.processed), Packet::AED(p) => set_or_extend(rest, p.deref_mut(), self.processed), @@ -6033,7 +6068,7 @@ impl<'a> PacketParser<'a> { } match self.packet.clone() { - Packet::SEIP(_) => { + Packet::SEIP(SEIP::V1(_)) => { // 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. @@ -6105,6 +6140,65 @@ impl<'a> PacketParser<'a> { Ok(()) }, + Packet::SEIP(SEIP::V2(seip)) => { + let chunk_size = + aead::chunk_size_usize(seip.chunk_size())?; + + // Read the first chunk and check whether we can + // decrypt it using the provided key. Don't actually + // consume them in case we can't. + { + // We need a bit more than one chunk so that + // `aead::Decryptor` won't see EOF and think that + // it has a partial block and it needs to verify + // the final chunk. + let amount = aead::chunk_size_usize( + seip.chunk_digest_size()? + + seip.aead().digest_size()? as u64)?; + + let data = self.data(amount)?; + let (message_key, schedule) = aead::SEIPv2Schedule::new( + key, + seip.symmetric_algo(), + seip.aead(), + chunk_size, + seip.salt())?; + + let dec = aead::Decryptor::new( + seip.symmetric_algo(), seip.aead(), chunk_size, + schedule, message_key, + &data[..cmp::min(data.len(), amount)])?; + let mut chunk = Vec::new(); + dec.take(seip.chunk_size() as u64).read_to_end(&mut chunk)?; + } + + // Ok, we can decrypt the data. Push a Decryptor and + // a HashedReader on the `BufferedReader` stack. + + // This can't fail, because we create a decryptor + // above with the same parameters. + let (message_key, schedule) = aead::SEIPv2Schedule::new( + key, + seip.symmetric_algo(), + seip.aead(), + chunk_size, + seip.salt())?; + + let reader = self.take_reader(); + let mut reader = aead::BufferedReaderDecryptor::with_cookie( + seip.symmetric_algo(), seip.aead(), chunk_size, + schedule, message_key, reader, Cookie::default()).unwrap(); + reader.cookie_mut().level = Some(self.recursion_depth()); + + t!("Pushing aead::Decryptor, level {:?}.", + reader.cookie_ref().level); + + self.reader = Box::new(reader); + self.processed = true; + + Ok(()) + }, + Packet::AED(AED::V1(aed)) => { let chunk_size = aead::chunk_size_usize(aed.chunk_size())?; diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index b42ae16b..ccc3fc84 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -120,6 +120,7 @@ use crate::{ key, OnePassSig, PKESK, + SEIP, SKESK, }, KeyHandle, @@ -2429,10 +2430,10 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { } } - let sym_algo_hint = if let Packet::AED(ref aed) = pp.packet { - Some(aed.symmetric_algo()) - } else { - None + let sym_algo_hint = match &pp.packet { + Packet::SEIP(SEIP::V2(seip)) => Some(seip.symmetric_algo()), + Packet::AED(aed) => Some(aed.symmetric_algo()), + _ => None, }; match pp.packet { @@ -4150,6 +4151,71 @@ xHUDBRY0WIQ+50WENDPP"; .with_policy(&p, crate::frozen_time(), helper)?; assert!(v.message_processed()); assert_eq!(v.helper_ref().good, 2); + + Ok(()) + } + + /// This sample packet is from RFC9760. + #[test] + fn v6skesk_v2seip_aes128_ocb() -> Result<()> { + sample_skesk6_packet( + SymmetricAlgorithm::AES128, + AEADAlgorithm::OCB, + "password", + "crypto-refresh/v6skesk-aes128-ocb.pgp", + b"Hello, world!") + } + + /// This sample packet is from RFC9760. + #[test] + fn v6skesk_v2seip_aes128_eax() -> Result<()> { + sample_skesk6_packet( + SymmetricAlgorithm::AES128, + AEADAlgorithm::EAX, + "password", + "crypto-refresh/v6skesk-aes128-eax.pgp", + b"Hello, world!") + } + + /// This sample packet is from RFCXXX. + #[test] + fn v6skesk_v2seip_aes128_gcm() -> Result<()> { + sample_skesk6_packet( + SymmetricAlgorithm::AES128, + AEADAlgorithm::GCM, + "password", + "crypto-refresh/v6skesk-aes128-gcm.pgp", + b"Hello, world!") + } + + fn sample_skesk6_packet(cipher: SymmetricAlgorithm, + aead: AEADAlgorithm, + password: &str, + name: &str, + plaintext: &[u8]) + -> Result<()> { + if ! (aead.is_supported() + && aead.supports_symmetric_algo(&cipher)) + { + eprintln!("Skipping test vector {:?}...", name); + return Ok(()); + } + + eprintln!("Test vector {:?}...", name); + + let p = &P::new(); + let password: Password = String::from(password).into(); + + let h = VHelper::for_decryption(0, 0, 0, 0, vec![], vec![], + vec![password]); + let mut d = DecryptorBuilder::from_bytes(crate::tests::file(name))? + .with_policy(p, None, h)?; + assert!(d.message_processed()); + + let mut content = Vec::new(); + d.read_to_end(&mut content).unwrap(); + assert_eq!(&content, plaintext); + Ok(()) } } diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs index 11133e01..a8cb5cf8 100644 --- a/openpgp/src/serialize.rs +++ b/openpgp/src/serialize.rs @@ -2929,7 +2929,36 @@ impl MarshalInto for SKESK6 { impl seal::Sealed for SEIP {} impl Marshal for SEIP { - /// Writes a serialized version of the specified `SEIP` + fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { + match self { + SEIP::V1(p) => p.serialize(o), + SEIP::V2(p) => p.serialize(o), + } + } +} + +impl NetLength for SEIP { + fn net_len(&self) -> usize { + match self { + SEIP::V1(p) => p.net_len(), + SEIP::V2(p) => p.net_len(), + } + } +} + +impl MarshalInto for SEIP { + 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 SEIP1 {} +impl Marshal for SEIP1 { + /// Writes a serialized version of the specified `SEIP1` /// packet to `o`. /// /// # Errors @@ -2940,7 +2969,7 @@ impl Marshal for SEIP { fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { match self.body() { Body::Unprocessed(bytes) => { - o.write_all(&[self.version()])?; + o.write_all(&[1])?; o.write_all(bytes)?; Ok(()) }, @@ -2951,7 +2980,7 @@ impl Marshal for SEIP { } } -impl NetLength for SEIP { +impl NetLength for SEIP1 { fn net_len(&self) -> usize { match self.body() { Body::Unprocessed(bytes) => 1 /* Version */ + bytes.len(), @@ -2960,7 +2989,65 @@ impl NetLength for SEIP { } } -impl MarshalInto for SEIP { +impl MarshalInto for SEIP1 { + 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 SEIP2 { + /// Writes the headers of the `SEIP2` data packet to `o`. + fn serialize_headers(&self, o: &mut dyn std::io::Write) -> Result<()> { + o.write_all(&[2, // Version. + self.symmetric_algo().into(), + self.aead().into(), + self.chunk_size().trailing_zeros() as u8 - 6])?; + o.write_all(self.salt())?; + Ok(()) + } +} + +impl seal::Sealed for SEIP2 {} +impl Marshal for SEIP2 { + /// Writes a serialized version of the specified `AED` + /// packet to `o`. + /// + /// # Errors + /// + /// Returns `Error::InvalidOperation` if this packet has children. + /// To construct an encrypted message, use + /// `serialize::stream::Encryptor`. + fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { + match self.body() { + Body::Unprocessed(bytes) => { + self.serialize_headers(o)?; + o.write_all(bytes)?; + Ok(()) + }, + _ => Err(Error::InvalidOperation( + "Cannot encrypt, use serialize::stream::Encryptor".into()) + .into()), + } + } +} + +impl NetLength for SEIP2 { + fn net_len(&self) -> usize { + match self.body() { + Body::Unprocessed(bytes) => + 4 // Headers. + + self.salt().len() + + bytes.len(), + _ => 0, + } + } +} + +impl MarshalInto for SEIP2 { fn serialized_len(&self) -> usize { self.net_len() } diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs index e5abe9a9..af6e88bb 100644 --- a/openpgp/src/serialize/stream.rs +++ b/openpgp/src/serialize/stream.rs @@ -2959,16 +2959,16 @@ impl<'a, 'b> Encryptor2<'a, 'b> { struct AEADParameters { algo: AEADAlgorithm, chunk_size: usize, - nonce: Box<[u8]>, + salt: [u8; 32], } let aead = if let Some(algo) = self.aead_algo { - let mut nonce = vec![0; algo.nonce_size()?]; - crypto::random(&mut nonce); + let mut salt = [0u8; 32]; + crypto::random(&mut salt); Some(AEADParameters { algo, chunk_size: Self::AEAD_CHUNK_SIZE, - nonce: nonce.into_boxed_slice(), + salt, }) } else { None @@ -3015,25 +3015,27 @@ impl<'a, 'b> Encryptor2<'a, 'b> { if let Some(aead) = aead { // Write the AED packet. - CTB::new(Tag::AED).serialize(&mut inner)?; + CTB::new(Tag::SEIP).serialize(&mut inner)?; let mut inner = PartialBodyFilter::new(Message::from(inner), Cookie::new(level)); - let aed = AED1::new(self.sym_algo, aead.algo, - aead.chunk_size as u64, aead.nonce)?; - aed.serialize_headers(&mut inner)?; + let seip = SEIP2::new(self.sym_algo, aead.algo, + aead.chunk_size as u64, aead.salt)?; + seip.serialize_headers(&mut inner)?; - use crate::crypto::aead::AEDv1Schedule; - let schedule = AEDv1Schedule::new( - aed.symmetric_algo(), aed.aead(), aead.chunk_size, aed.iv())?; + use crate::crypto::aead::SEIPv2Schedule; + let (message_key, schedule) = SEIPv2Schedule::new( + &sk, + seip.symmetric_algo(), seip.aead(), aead.chunk_size, + seip.salt())?; writer::AEADEncryptor::new( inner, Cookie::new(level), - aed.symmetric_algo(), - aed.aead(), + seip.symmetric_algo(), + seip.aead(), aead.chunk_size, schedule, - sk, + message_key, ) } else { // Write the SEIP packet. -- cgit v1.2.3