summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-02-14 19:37:54 +0100
committerJustus Winter <justus@sequoia-pgp.org>2024-03-13 10:59:50 +0100
commitdc7a668bea6bc45fdba5bfe2565d1dcc4966251a (patch)
treed5403856925761034fceea25f897438c22b99f53
parent44c6bb25f967f92b46b03e96efe9050eff1c2743 (diff)
openpgp: Implement SKESKv6 from the crypto refresh.
- Use HKDF in the SKESKv6 to provide key separation. See https://gitlab.com/openpgp-wg/rfc4880bis/-/commit/a42ea9c05479cc8b0424c85b865d796d2250586c
-rw-r--r--openpgp/src/packet/mod.rs10
-rw-r--r--openpgp/src/packet/prelude.rs2
-rw-r--r--openpgp/src/packet/skesk.rs375
-rw-r--r--openpgp/src/parse.rs67
-rw-r--r--openpgp/src/parse/stream.rs2
-rw-r--r--openpgp/src/serialize.rs42
-rw-r--r--openpgp/src/serialize/stream.rs2
-rw-r--r--openpgp/tests/data/crypto-refresh/v6skesk-aes128-eax.pgp7
-rw-r--r--openpgp/tests/data/crypto-refresh/v6skesk-aes128-gcm.pgp7
-rw-r--r--openpgp/tests/data/crypto-refresh/v6skesk-aes128-ocb.pgp7
10 files changed, 248 insertions, 273 deletions
diff --git a/openpgp/src/packet/mod.rs b/openpgp/src/packet/mod.rs
index af927464..6956791c 100644
--- a/openpgp/src/packet/mod.rs
+++ b/openpgp/src/packet/mod.rs
@@ -480,7 +480,7 @@ impl Deref for Packet {
Packet::CompressedData(ref packet) => &packet.common,
Packet::PKESK(ref packet) => &packet.common,
Packet::SKESK(SKESK::V4(ref packet)) => &packet.common,
- Packet::SKESK(SKESK::V5(ref packet)) => &packet.skesk4.common,
+ Packet::SKESK(SKESK::V6(ref packet)) => &packet.skesk4.common,
Packet::SEIP(ref packet) => &packet.common,
#[allow(deprecated)]
Packet::MDC(ref packet) => &packet.common,
@@ -509,7 +509,7 @@ impl DerefMut for Packet {
Packet::CompressedData(ref mut packet) => &mut packet.common,
Packet::PKESK(ref mut packet) => &mut packet.common,
Packet::SKESK(SKESK::V4(ref mut packet)) => &mut packet.common,
- Packet::SKESK(SKESK::V5(ref mut packet)) => &mut packet.skesk4.common,
+ Packet::SKESK(SKESK::V6(ref mut packet)) => &mut packet.skesk4.common,
Packet::SEIP(ref mut packet) => &mut packet.common,
#[allow(deprecated)]
Packet::MDC(ref mut packet) => &mut packet.common,
@@ -1292,10 +1292,10 @@ impl DerefMut for PKESK {
pub enum SKESK {
/// SKESK packet version 4.
V4(self::skesk::SKESK4),
- /// SKESK packet version 5.
+ /// SKESK packet version 6.
///
/// This feature is [experimental](super#experimental-features).
- V5(self::skesk::SKESK5),
+ V6(self::skesk::SKESK6),
}
assert_send_and_sync!(SKESK);
@@ -1304,7 +1304,7 @@ impl SKESK {
pub fn version(&self) -> u8 {
match self {
SKESK::V4(_) => 4,
- SKESK::V5(_) => 5,
+ SKESK::V6(_) => 6,
}
}
}
diff --git a/openpgp/src/packet/prelude.rs b/openpgp/src/packet/prelude.rs
index 1e41b16a..c06339c1 100644
--- a/openpgp/src/packet/prelude.rs
+++ b/openpgp/src/packet/prelude.rs
@@ -57,6 +57,6 @@ pub use crate::packet::{
signature::Signature6,
signature::SignatureBuilder,
skesk::SKESK4,
- skesk::SKESK5,
+ skesk::SKESK6,
user_attribute,
};
diff --git a/openpgp/src/packet/skesk.rs b/openpgp/src/packet/skesk.rs
index 5331604e..98075970 100644
--- a/openpgp/src/packet/skesk.rs
+++ b/openpgp/src/packet/skesk.rs
@@ -12,7 +12,13 @@ use std::ops::{Deref, DerefMut};
use quickcheck::{Arbitrary, Gen};
use crate::Result;
-use crate::crypto::{self, S2K, Password, SessionKey};
+use crate::crypto::{
+ self,
+ S2K,
+ Password,
+ SessionKey,
+ backend::{Backend, interface::Kdf},
+};
use crate::crypto::aead::CipherOp;
use crate::Error;
use crate::types::{
@@ -31,7 +37,8 @@ impl SKESK {
{
match self {
SKESK::V4(ref s) => s.decrypt(password),
- SKESK::V5(ref s) => s.decrypt(password),
+ SKESK::V6(ref s) =>
+ Ok((SymmetricAlgorithm::Unencrypted, s.decrypt(password)?)),
}
}
}
@@ -42,7 +49,7 @@ impl Arbitrary for SKESK {
if bool::arbitrary(g) {
SKESK::V4(SKESK4::arbitrary(g))
} else {
- SKESK::V5(SKESK5::arbitrary(g))
+ SKESK::V6(SKESK6::arbitrary(g))
}
}
}
@@ -60,7 +67,7 @@ pub struct SKESK4 {
pub(crate) common: packet::Common,
/// Packet version. Must be 4 or 5.
///
- /// This struct is also used by SKESK5, hence we have a version
+ /// This struct is also used by SKESK6, hence we have a version
/// field.
version: u8,
/// Symmetric algorithm used to encrypt the session key.
@@ -308,75 +315,18 @@ impl Arbitrary for SKESK4 {
/// [Section 5.3 of RFC 4880]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-05#section-5.3
///
/// This feature is [experimental](super::super#experimental-features).
-#[derive(Clone, Debug)]
-pub struct SKESK5 {
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct SKESK6 {
/// Common fields.
pub(crate) skesk4: SKESK4,
/// AEAD algorithm.
aead_algo: AEADAlgorithm,
/// Initialization vector for the AEAD algorithm.
- ///
- /// If we recognized the S2K object during parsing, we can
- /// successfully parse the data into S2K, AEAED IV, and
- /// ciphertext. However, if we do not recognize the S2K type, we
- /// do not know how large its parameters are, so we cannot cleanly
- /// parse it, and have to accept that the S2K's body bleeds into
- /// the rest of the data. In this case, the raw data is put into
- /// the `esk` field, and `aead_iv` is set to `None`.
- aead_iv: Option<Box<[u8]>>,
- /// Digest for the AEAD algorithm.
- aead_digest: Box<[u8]>,
-}
-assert_send_and_sync!(SKESK5);
-
-// Because the S2K, IV, and ESK cannot be cleanly separated at parse
-// time, we need to carefully compare and hash SKESK5 packets.
-
-impl PartialEq for SKESK5 {
- fn eq(&self, other: &SKESK5) -> bool {
- self.skesk4.version == other.skesk4.version
- && self.skesk4.sym_algo == other.skesk4.sym_algo
- && self.aead_algo == other.aead_algo
- && self.aead_digest == other.aead_digest
- // Treat S2K, IV, and ESK as opaque blob.
- && {
- // XXX: This would be nicer without the allocations.
- use crate::serialize::MarshalInto;
- let mut a = self.skesk4.s2k.to_vec().unwrap();
- let mut b = other.skesk4.s2k.to_vec().unwrap();
- if let Ok(iv) = self.aead_iv() {
- a.extend_from_slice(iv);
- }
- if let Ok(iv) = other.aead_iv() {
- b.extend_from_slice(iv);
- }
- a.extend_from_slice(self.skesk4.raw_esk());
- b.extend_from_slice(other.skesk4.raw_esk());
- a == b
- }
- }
+ aead_iv: Box<[u8]>,
}
+assert_send_and_sync!(SKESK6);
-impl Eq for SKESK5 {}
-
-impl std::hash::Hash for SKESK5 {
- fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
- self.skesk4.version.hash(state);
- self.skesk4.sym_algo.hash(state);
- self.aead_digest.hash(state);
- // Treat S2K, IV, and ESK as opaque blob.
- // XXX: This would be nicer without the allocations.
- use crate::serialize::MarshalInto;
- let mut a = self.skesk4.s2k.to_vec().unwrap();
- if let Some(iv) = self.aead_iv.as_ref() {
- a.extend_from_slice(iv);
- }
- a.extend_from_slice(self.skesk4.raw_esk());
- a.hash(state);
- }
-}
-
-impl Deref for SKESK5 {
+impl Deref for SKESK6 {
type Target = SKESK4;
fn deref(&self) -> &Self::Target {
@@ -384,49 +334,33 @@ impl Deref for SKESK5 {
}
}
-impl DerefMut for SKESK5 {
+impl DerefMut for SKESK6 {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.skesk4
}
}
-impl SKESK5 {
+impl SKESK6 {
/// Creates a new SKESK version 5 packet.
///
/// The given symmetric algorithm is the one used to encrypt the
/// session key.
- pub fn new(esk_algo: SymmetricAlgorithm, esk_aead: AEADAlgorithm,
- s2k: S2K, iv: Box<[u8]>, esk: Box<[u8]>, digest: Box<[u8]>)
+ pub fn new(sym_algo: SymmetricAlgorithm,
+ aead_algo: AEADAlgorithm,
+ s2k: S2K,
+ aead_iv: Box<[u8]>,
+ esk: Box<[u8]>)
-> Result<Self> {
- Self::new_raw(esk_algo, esk_aead, s2k, Ok((iv, esk)), digest)
- }
-
- /// Creates a new SKESK version 5 packet.
- ///
- /// The given symmetric algorithm is the one used to encrypt the
- /// session key.
- pub(crate) fn new_raw(esk_algo: SymmetricAlgorithm, esk_aead: AEADAlgorithm,
- s2k: S2K,
- iv_esk: std::result::Result<(Box<[u8]>, Box<[u8]>),
- Box<[u8]>>,
- digest: Box<[u8]>)
- -> Result<Self> {
- let (iv, esk) = match iv_esk {
- Ok((iv, esk)) => (Some(iv), Ok(Some(esk))),
- Err(raw) => (None, Err(raw)),
- };
-
- Ok(SKESK5{
- skesk4: SKESK4{
+ Ok(SKESK6 {
+ skesk4: SKESK4 {
common: Default::default(),
- version: 5,
- sym_algo: esk_algo,
+ version: 6,
+ sym_algo,
s2k,
- esk,
+ esk: Ok(Some(esk)),
},
- aead_algo: esk_aead,
- aead_iv: iv,
- aead_digest: digest,
+ aead_algo,
+ aead_iv,
})
}
@@ -456,64 +390,55 @@ impl SKESK5 {
}
// Derive key and make a cipher.
+ let ad = [0xc3, 6, esk_algo.into(), esk_aead.into()];
let key = s2k.derive_key(password, esk_algo.key_size()?)?;
- let mut iv = vec![0u8; esk_aead.nonce_size()?];
- crypto::random(&mut iv);
- let aad = [0xc3, 5, esk_algo.into(), esk_aead.into()];
- let mut ctx = esk_aead.context(esk_algo, &key, &aad, &iv,
- CipherOp::Encrypt)?;
+
+ let mut kek: SessionKey = vec![0; esk_algo.key_size()?].into();
+ Backend::hkdf_sha256(&key, None, &ad, &mut kek)?;
+
// Encrypt the session key with the KEK.
+ let mut iv = vec![0u8; esk_aead.nonce_size()?];
+ crypto::random(&mut iv);
+ let mut ctx =
+ esk_aead.context(esk_algo, &kek, &ad, &iv, CipherOp::Encrypt)?;
let mut esk_digest =
vec![0u8; session_key.len() + esk_aead.digest_size()?];
ctx.encrypt_seal(&mut esk_digest, session_key)?;
- let digest = esk_digest[session_key.len()..].to_vec();
- let esk = {
- crate::vec_truncate(&mut esk_digest, session_key.len());
- esk_digest
- };
-
- SKESK5::new(esk_algo, esk_aead, s2k, iv.into_boxed_slice(), esk.into(),
- digest.into_boxed_slice())
+ // Attach digest to the ESK, we model it as one.
+ SKESK6::new(esk_algo, esk_aead, s2k, iv.into_boxed_slice(),
+ esk_digest.into())
}
- /// Derives the key inside this `SKESK5` from `password`.
+ /// Derives the key inside this `SKESK6` from `password`.
///
/// Returns a tuple containing a placeholder symmetric cipher and
- /// the key itself. `SKESK5` packets do not contain the symmetric
+ /// the key itself. `SKESK6` packets do not contain the symmetric
/// cipher algorithm and instead rely on the `AED` packet that
/// contains it.
- // XXX: This function should return Result<SessionKey>, but then
- // SKESK::decrypt must return an
- // Result<(Option<SymmetricAlgorithm>, _)> and
- // DecryptionHelper::decrypt and PacketParser::decrypt must be
- // adapted as well.
pub fn decrypt(&self, password: &Password)
- -> Result<(SymmetricAlgorithm, SessionKey)> {
+ -> Result<SessionKey> {
let key = self.s2k().derive_key(password,
self.symmetric_algo().key_size()?)?;
- if let Some(esk) = self.esk()? {
- // Use the derived key to decrypt the ESK.
- let aad = [0xc3, 5 /* Version. */, self.symmetric_algo().into(),
- self.aead_algo.into()];
- let mut cipher = self.aead_algo.context(
- self.symmetric_algo(), &key, &aad, self.aead_iv()?,
- CipherOp::Decrypt)?;
-
- let mut plain: SessionKey = vec![0; esk.len()].into();
- let mut chunk =
- Vec::with_capacity(esk.len() + self.aead_digest.len());
- chunk.extend_from_slice(esk);
- chunk.extend_from_slice(&self.aead_digest);
- cipher.decrypt_verify(&mut plain, &chunk)?;
- Ok((SymmetricAlgorithm::Unencrypted, plain))
- } else {
- Err(Error::MalformedPacket(
- "No encrypted session key in v5 SKESK packet".into())
- .into())
- }
+ let mut kek: SessionKey =
+ vec![0; self.symmetric_algo().key_size()?].into();
+ let ad = [0xc3,
+ 6 /* Version. */,
+ self.symmetric_algo().into(),
+ self.aead_algo.into()];
+ Backend::hkdf_sha256(&key, None, &ad, &mut kek)?;
+
+ // Use the derived key to decrypt the ESK.
+ let mut cipher = self.aead_algo.context(
+ self.symmetric_algo(), &kek, &ad, self.aead_iv(),
+ CipherOp::Decrypt)?;
+
+ let mut plain: SessionKey =
+ vec![0; self.esk().len() - self.aead_algo.digest_size()?].into();
+ cipher.decrypt_verify(&mut plain, self.esk())?;
+ Ok(plain)
}
/// Gets the AEAD algorithm.
@@ -527,66 +452,59 @@ impl SKESK5 {
}
/// Gets the AEAD initialization vector.
- ///
- /// If the [`S2K`] mechanism is not supported by Sequoia, this
- /// function will fail. Note that the information is not lost,
- /// but stored in the packet. If the packet is serialized again,
- /// it is written out.
- ///
- /// [`S2K`]: super::super::crypto::S2K
- pub fn aead_iv(&self) -> Result<&[u8]> {
- self.aead_iv.as_ref()
- .map(|iv| &iv[..])
- .ok_or_else(|| Error::MalformedPacket(
- format!("Unknown S2K: {:?}", self.s2k)).into())
+ pub fn aead_iv(&self) -> &[u8] {
+ &self.aead_iv
}
/// Sets the AEAD initialization vector.
- pub fn set_aead_iv(&mut self, iv: Box<[u8]>) -> Option<Box<[u8]>> {
- ::std::mem::replace(&mut self.aead_iv, Some(iv))
+ pub fn set_aead_iv(&mut self, iv: Box<[u8]>) -> Box<[u8]> {
+ ::std::mem::replace(&mut self.aead_iv, iv)
}
- /// Gets the AEAD digest.
- pub fn aead_digest(&self) -> &[u8] {
- &self.aead_digest
+ /// Gets the encrypted session key.
+ pub fn esk(&self) -> &[u8] {
+ self.skesk4.raw_esk()
}
- /// Sets the AEAD digest.
- pub fn set_aead_digest(&mut self, digest: Box<[u8]>) -> Box<[u8]> {
- ::std::mem::replace(&mut self.aead_digest, digest)
+ /// Sets the encrypted session key.
+ pub fn set_esk(&mut self, esk: Box<[u8]>) -> Box<[u8]> {
+ ::std::mem::replace(&mut self.esk, Ok(Some(esk)))
+ .expect("v6 SKESK can always be parsed")
+ .expect("v6 SKESK packets always have an ESK")
}
}
-impl From<SKESK5> for super::SKESK {
- fn from(p: SKESK5) -> Self {
- super::SKESK::V5(p)
+impl From<SKESK6> for super::SKESK {
+ fn from(p: SKESK6) -> Self {
+ super::SKESK::V6(p)
}
}
-impl From<SKESK5> for Packet {
- fn from(s: SKESK5) -> Self {
- Packet::SKESK(SKESK::V5(s))
+impl From<SKESK6> for Packet {
+ fn from(s: SKESK6) -> Self {
+ Packet::SKESK(SKESK::V6(s))
}
}
#[cfg(test)]
-impl Arbitrary for SKESK5 {
+impl Arbitrary for SKESK6 {
fn arbitrary(g: &mut Gen) -> Self {
let algo = AEADAlgorithm::const_default();
let mut iv = vec![0u8; algo.nonce_size().unwrap()];
for b in iv.iter_mut() {
*b = u8::arbitrary(g);
}
- let mut digest = vec![0u8; algo.digest_size().unwrap()];
- for b in digest.iter_mut() {
+ let esk_len =
+ (u8::arbitrary(g) % 64) as usize + algo.digest_size().unwrap();
+ let mut esk = vec![0u8; esk_len];
+ for b in esk.iter_mut() {
*b = u8::arbitrary(g);
}
- SKESK5::new(SymmetricAlgorithm::arbitrary(g),
+ SKESK6::new(SymmetricAlgorithm::arbitrary(g),
algo,
S2K::arbitrary(g),
- iv.into_boxed_slice(),
- Vec::<u8>::arbitrary(g).into(),
- digest.into_boxed_slice())
+ iv.into(),
+ esk.into())
.unwrap()
}
}
@@ -596,61 +514,98 @@ mod test {
use super::*;
use crate::PacketPile;
use crate::parse::Parse;
- use crate::serialize::{Marshal, MarshalInto};
+ use crate::serialize::MarshalInto;
quickcheck! {
- fn roundtrip(p: SKESK) -> bool {
+ fn roundtrip_v4(p: SKESK4) -> bool {
+ let p = SKESK::from(p);
let q = SKESK::from_bytes(&p.to_vec().unwrap()).unwrap();
assert_eq!(p, q);
true
}
}
+ quickcheck! {
+ fn roundtrip_v6(p: SKESK6) -> bool {
+ let p = SKESK::from(p);
+ let q = SKESK::from_bytes(&p.to_vec().unwrap()).unwrap();
+ assert_eq!(p, q);
+ true
+ }
+ }
+
+ /// This sample packet is from RFCXXX.
+ #[test]
+ fn v6skesk_aes128_ocb() -> Result<()> {
+ sample_skesk6_packet(
+ SymmetricAlgorithm::AES128,
+ AEADAlgorithm::OCB,
+ "crypto-refresh/v6skesk-aes128-ocb.pgp",
+ b"\xe8\x0d\xe2\x43\xa3\x62\xd9\x3b\
+ \x9d\xc6\x07\xed\xe9\x6a\x73\x56",
+ b"\x28\xe7\x9a\xb8\x23\x97\xd3\xc6\
+ \x3d\xe2\x4a\xc2\x17\xd7\xb7\x91")
+ }
+
+ /// This sample packet is from RFCXXX.
#[test]
- fn sample_skesk5_packet() {
- // This sample packet is from RFC4880bis-05, section A.3.
+ fn v6skesk_aes128_eax() -> Result<()> {
+ sample_skesk6_packet(
+ SymmetricAlgorithm::AES128,
+ AEADAlgorithm::EAX,
+ "crypto-refresh/v6skesk-aes128-eax.pgp",
+ b"\x15\x49\x67\xe5\x90\xaa\x1f\x92\
+ \x3e\x1c\x0a\xc6\x4c\x88\xf2\x3d",
+ b"\x38\x81\xba\xfe\x98\x54\x12\x45\
+ \x9b\x86\xc3\x6f\x98\xcb\x9a\x5e")
+ }
+
+ /// This sample packet is from RFCXXX.
+ #[test]
+ fn v6skesk_aes128_gcm() -> Result<()> {
+ sample_skesk6_packet(
+ SymmetricAlgorithm::AES128,
+ AEADAlgorithm::GCM,
+ "crypto-refresh/v6skesk-aes128-gcm.pgp",
+ b"\x25\x02\x81\x71\x5b\xba\x78\x28\
+ \xef\x71\xef\x64\xc4\x78\x47\x53",
+ b"\x19\x36\xfc\x85\x68\x98\x02\x74\
+ \xbb\x90\x0d\x83\x19\x36\x0c\x77")
+ }
+
+ fn sample_skesk6_packet(cipher: SymmetricAlgorithm,
+ aead: AEADAlgorithm,
+ name: &str,
+ derived_key: &[u8],
+ session_key: &[u8])
+ -> Result<()> {
let password: Password = String::from("password").into();
- let raw = [
- // Packet header:
- 0xc3, 0x3e,
-
- // Version, algorithms, S2K fields:
- 0x05, 0x07, 0x01, 0x03, 0x08, 0xcd, 0x5a, 0x9f,
- 0x70, 0xfb, 0xe0, 0xbc, 0x65, 0x90,
-
- // AEAD IV:
- 0xbc, 0x66, 0x9e, 0x34, 0xe5, 0x00, 0xdc, 0xae,
- 0xdc, 0x5b, 0x32, 0xaa, 0x2d, 0xab, 0x02, 0x35,
-
- // AEAD encrypted CEK:
- 0x9d, 0xee, 0x19, 0xd0, 0x7c, 0x34, 0x46, 0xc4,
- 0x31, 0x2a, 0x34, 0xae, 0x19, 0x67, 0xa2, 0xfb,
-
- // Authentication tag:
- 0x7e, 0x92, 0x8e, 0xa5, 0xb4, 0xfa, 0x80, 0x12,
- 0xbd, 0x45, 0x6d, 0x17, 0x38, 0xc6, 0x3c, 0x36,
- ];
let packets: Vec<Packet> =
- PacketPile::from_bytes(&raw[..]).unwrap().into_children().collect();
- assert_eq!(packets.len(), 1);
- if let Packet::SKESK(SKESK::V5(ref s)) = packets[0] {
- assert_eq!(&s.s2k().derive_key(
- &password, s.symmetric_algo().key_size().unwrap()).unwrap()[..],
- &[0xb2, 0x55, 0x69, 0xb9, 0x54, 0x32, 0x45, 0x66,
- 0x45, 0x27, 0xc4, 0x97, 0x6e, 0x7a, 0x5d, 0x6e][..]);
-
- if AEADAlgorithm::EAX.is_supported() {
- assert_eq!(&s.decrypt(&password).unwrap().1[..],
- &[0x86, 0xf1, 0xef, 0xb8, 0x69, 0x52, 0x32, 0x9f,
- 0x24, 0xac, 0xd3, 0xbf, 0xd0, 0xe5, 0x34, 0x6d][..]);
+ PacketPile::from_bytes(
+ crate::tests::file(name))?
+ .into_children().collect();
+ assert_eq!(packets.len(), 2);
+ if let Packet::SKESK(SKESK::V6(ref s)) = packets[0] {
+ let derived = s.s2k().derive_key(
+ &password, s.symmetric_algo().key_size()?)?;
+ eprintln!("derived: {:x?}", &derived[..]);
+ assert_eq!(&derived[..], derived_key);
+
+ if aead.is_supported()
+ && aead.supports_symmetric_algo(&cipher)
+ {
+ let sk = s.decrypt(&password)?;
+ eprintln!("sk: {:x?}", &sk[..]);
+ assert_eq!(&sk[..], session_key);
+ } else {
+ eprintln!("{}-{} is not supported, skipping decryption.",
+ cipher, aead);
}
} else {
- panic!("bad packet");
+ panic!("bad packet, expected v6 SKESK: {:?}", packets[0]);
}
- let mut serialized = Vec::new();
- packets[0].serialize(&mut serialized).unwrap();
- assert_eq!(&raw[..], &serialized[..]);
+ Ok(())
}
/// Tests various S2K methods, with and without encrypted session
diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs
index 62f1e225..935d2412 100644
--- a/openpgp/src/parse.rs
+++ b/openpgp/src/parse.rs
@@ -3307,7 +3307,7 @@ impl SKESK {
let version = php_try!(php.parse_u8("version"));
match version {
4 => SKESK4::parse(php),
- 5 => SKESK5::parse(php),
+ 6 => SKESK6::parse(php),
_ => php.fail("unknown version"),
}
}
@@ -3342,58 +3342,49 @@ impl SKESK4 {
}
}
-impl SKESK5 {
+impl SKESK6 {
/// Parses the body of an SK-ESK packet.
fn parse(mut php: PacketHeaderParser)
-> Result<PacketParser>
{
make_php_try!(php);
+
+ // Octet count of the following 5 fields.
+ let parameter_len = php_try!(php.parse_u8("parameter_len"));
+ if parameter_len < 1 + 1 + 1 + 2 /* S2K */ + 12 /* IV */ {
+ return php.fail("expected at least 16 parameter octets");
+ }
+
let sym_algo: SymmetricAlgorithm =
php_try!(php.parse_u8("sym_algo")).into();
let aead_algo: AEADAlgorithm =
php_try!(php.parse_u8("aead_algo")).into();
- let s2k = php_try!(S2K::parse_v4(&mut php));
- let s2k_supported = s2k.is_supported();
- let iv_size = php_try!(aead_algo.nonce_size());
- let digest_size = php_try!(aead_algo.digest_size());
-
- // The rest of the packet is (potentially) the S2K
- // parameters, the AEAD IV, the ESK, and the AEAD
- // digest. We don't know the size of the S2K
- // parameters if the S2K method is not supported, and
- // we don't know the size of the ESK.
- let mut esk = php_try!(php.reader.steal_eof()
- .map_err(anyhow::Error::from));
- let aead_iv = if s2k_supported && esk.len() >= iv_size {
- // We know the S2K method, so the parameters have
- // been parsed into the S2K object. So, `esk`
- // starts with iv_size bytes of IV.
- let mut iv = esk;
- esk = iv.split_off(iv_size);
- iv
- } else {
- Vec::with_capacity(0) // A dummy value.
- };
- let l = esk.len();
- let aead_digest = esk.split_off(l.saturating_sub(digest_size));
- // Now fix the map.
- if s2k_supported {
- php.field("aead_iv", iv_size);
+ // The S2K object's length and the S2K.
+ let s2k_len = php_try!(php.parse_u8("s2k_len"));
+ if parameter_len < 1 + 1 + 1 + s2k_len + 12 /* IV */ {
+ return php.fail("S2K overflows parameter count");
}
- php.field("esk", esk.len());
- php.field("aead_digest", aead_digest.len());
- let skesk = php_try!(SKESK5::new_raw(
+ let s2k = php_try!(S2K::parse_v6(&mut php, s2k_len));
+
+ // And the IV.
+ let iv =
+ if let Some(iv_len) = parameter_len.checked_sub(1 + 1 + 1 + s2k_len) {
+ php_try!(php.parse_bytes("iv", iv_len as usize)).into()
+ } else {
+ return php.fail("IV overflows parameter count");
+ };
+
+ // Finally, the ESK including the AEAD tag.
+ let esk = php_try!(php.parse_bytes_eof("esk")).into();
+
+ let skesk = php_try!(SKESK6::new(
sym_algo,
aead_algo,
s2k,
- if s2k_supported {
- Ok((aead_iv.into(), esk.into()))
- } else {
- Err(esk.into())
- },
- aead_digest.into_boxed_slice(),
+ iv,
+ esk,
));
php.ok(skesk.into())
diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs
index d3647912..b42ae16b 100644
--- a/openpgp/src/parse/stream.rs
+++ b/openpgp/src/parse/stream.rs
@@ -2448,7 +2448,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 SKESK5 packet.
+ // the dummy one from the SKESK6 packet.
let algo = sym_algo_hint.unwrap_or(algo);
let result = pp.decrypt(algo, secret);
t!("pp.decrypt({:?}, {:?}) => {:?}",
diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs
index 65bac0fe..11133e01 100644
--- a/openpgp/src/serialize.rs
+++ b/openpgp/src/serialize.rs
@@ -2818,7 +2818,7 @@ impl Marshal for SKESK {
fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> {
match self {
SKESK::V4(ref s) => s.serialize(o),
- SKESK::V5(ref s) => s.ser