summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-04-25 18:35:48 +0200
committerJustus Winter <justus@sequoia-pgp.org>2024-03-13 10:59:50 +0100
commit802c5c18feea6c79557b386127f06d1a01e286c8 (patch)
tree8af5b38fdbf379a8a84d729a164cd5f780fdb875
parent6797f544f742929635c79241b14a626ced43d86a (diff)
wip pkeskv6
-rw-r--r--guide/src/chapter_02.md8
-rw-r--r--ipc/examples/gpg-agent-decrypt.rs6
-rw-r--r--ipc/tests/gpg-agent.rs7
-rw-r--r--openpgp/benches/common/decrypt.rs7
-rw-r--r--openpgp/examples/decrypt-with.rs4
-rw-r--r--openpgp/examples/generate-encrypt-decrypt.rs2
-rw-r--r--openpgp/examples/reply-encrypted.rs10
-rw-r--r--openpgp/src/keyhandle.rs10
-rw-r--r--openpgp/src/packet/mod.rs84
-rw-r--r--openpgp/src/packet/pkesk.rs225
-rw-r--r--openpgp/src/packet/pkesk/v6.rs183
-rw-r--r--openpgp/src/packet/prelude.rs5
-rw-r--r--openpgp/src/packet/skesk.rs11
-rw-r--r--openpgp/src/parse.rs95
-rw-r--r--openpgp/src/parse/stream.rs46
-rw-r--r--openpgp/src/policy.rs8
-rw-r--r--openpgp/src/serialize.rs59
-rw-r--r--openpgp/src/serialize/stream.rs40
-rw-r--r--openpgp/tests/secret-leak-detector/detector.rs2
19 files changed, 674 insertions, 138 deletions
diff --git a/guide/src/chapter_02.md b/guide/src/chapter_02.md
index 493528d7..ffde7ace 100644
--- a/guide/src/chapter_02.md
+++ b/guide/src/chapter_02.md
@@ -128,7 +128,7 @@ fn main() -> openpgp::Result<()> {
# sym_algo: Option<SymmetricAlgorithm>,
# mut decrypt: D)
# -> openpgp::Result<Option<openpgp::Fingerprint>>
-# where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+# where D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool
# {
# // The encryption key is the first and only subkey.
# let key = self.secret.keys().unencrypted_secret()
@@ -276,7 +276,7 @@ fn generate() -> openpgp::Result<openpgp::Cert> {
# sym_algo: Option<SymmetricAlgorithm>,
# mut decrypt: D)
# -> openpgp::Result<Option<openpgp::Fingerprint>>
-# where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+# where D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool
# {
# // The encryption key is the first and only subkey.
# let key = self.secret.keys().unencrypted_secret()
@@ -424,7 +424,7 @@ fn encrypt(policy: &dyn Policy,
# sym_algo: Option<SymmetricAlgorithm>,
# mut decrypt: D)
# -> openpgp::Result<Option<openpgp::Fingerprint>>
-# where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+# where D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool
# {
# // The encryption key is the first and only subkey.
# let key = self.secret.keys().unencrypted_secret()
@@ -586,7 +586,7 @@ impl<'a> DecryptionHelper for Helper<'a> {
sym_algo: Option<SymmetricAlgorithm>,
mut decrypt: D)
-> openpgp::Result<Option<openpgp::Fingerprint>>
- where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+ where D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool
{
// The encryption key is the first and only subkey.
let key = self.secret.keys().unencrypted_secret()
diff --git a/ipc/examples/gpg-agent-decrypt.rs b/ipc/examples/gpg-agent-decrypt.rs
index f0777718..d996fb56 100644
--- a/ipc/examples/gpg-agent-decrypt.rs
+++ b/ipc/examples/gpg-agent-decrypt.rs
@@ -122,11 +122,13 @@ impl DecryptionHelper for Helper {
sym_algo: Option<SymmetricAlgorithm>,
mut decrypt: D)
-> openpgp::Result<Option<openpgp::Fingerprint>>
- where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+ where D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool
{
// Try each PKESK until we succeed.
for pkesk in pkesks {
- if let Some(pair) = self.keys.get_mut(pkesk.recipient()) {
+ if let Some(pair) = pkesk.recipient()
+ .and_then(|r| self.keys.get_mut(&r.into()))
+ {
if pkesk.decrypt(pair, sym_algo)
.map(|(algo, session_key)| decrypt(algo, &session_key))
.unwrap_or(false)
diff --git a/ipc/tests/gpg-agent.rs b/ipc/tests/gpg-agent.rs
index 8ec4482d..d7f8f8c1 100644
--- a/ipc/tests/gpg-agent.rs
+++ b/ipc/tests/gpg-agent.rs
@@ -396,7 +396,7 @@ fn decrypt(also_try_explicit_async: bool) -> openpgp::Result<()> {
sym_algo: Option<SymmetricAlgorithm>,
mut decrypt: D)
-> openpgp::Result<Option<openpgp::Fingerprint>>
- where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+ where D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool
{
// We only gave the cert to GnuPG, the agent doesn't
// have the secret.
@@ -424,7 +424,10 @@ fn decrypt(also_try_explicit_async: bool) -> openpgp::Result<()> {
}
for pkesk in pkesks {
- if *pkesk.recipient() != keypair.public().keyid() {
+ if pkesk.recipient()
+ .map(|h| ! h.aliases(keypair.public().key_handle()))
+ .unwrap_or(true)
+ {
continue;
}
diff --git a/openpgp/benches/common/decrypt.rs b/openpgp/benches/common/decrypt.rs
index ef8a2db8..dc3cc8ab 100644
--- a/openpgp/benches/common/decrypt.rs
+++ b/openpgp/benches/common/decrypt.rs
@@ -39,7 +39,7 @@ impl DecryptionHelper for PasswordHelper {
mut decrypt: D,
) -> Result<Option<Fingerprint>>
where
- D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool,
+ D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool,
{
// Finally, try to decrypt using the SKESKs.
for skesk in skesks {
@@ -135,7 +135,7 @@ impl DecryptionHelper for CertHelper<'_> {
mut decrypt: D,
) -> Result<Option<Fingerprint>>
where
- D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool,
+ D: FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool,
{
let p = &StandardPolicy::new();
@@ -158,7 +158,8 @@ impl DecryptionHelper for CertHelper<'_> {
.filter_map(|key| {
pkesks
.iter()
- .find(|pkesk| pkesk.recipient() == &key.keyid())
+ .find(|pkesk| pkesk.recipient().map(
+ |r| r.aliases(&key.key_handle())).unwrap_or(false))
.map(|pkesk| (pkesk, key))
})
.find(|(pkesk, key)| {
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<SymmetricAlgorithm>,
mut decrypt: D)
-> openpgp::Result<Option<openpgp::Fingerprint>>
- where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+ where D: FnMut(Option<SymmetricAlgorithm>, &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 2e6c6b12..f1e8e38b 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<SymmetricAlgorithm>,
mut decrypt: D)
-> openpgp::Result<Option<openpgp::Fingerprint>>
- where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+ where D: FnMut(Option<SymmetricAlgorithm>, &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 61d41d11..f711a93b 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 = Encryptor2::with_session_key(message, algo, sk)?
+ let message = Encryptor2::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<KeyID, (Fingerprint, KeyPair)>,
- recycling_bin: Option<(SymmetricAlgorithm, SessionKey, Vec<PKESK>)>,
+ recycling_bin: Option<(Option<SymmetricAlgorithm>, SessionKey, Vec<PKESK>)>,
}
impl Helper {
@@ -161,13 +161,13 @@ impl DecryptionHelper for Helper {
sym_algo: Option<SymmetricAlgorithm>,
mut decrypt: D)
-> openpgp::Result<Option<openpgp::Fingerprint>>
- where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+ where D: FnMut(Option<SymmetricAlgorithm>, &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 32f23536..f1aa2766 100644
--- a/openpgp/src/keyhandle.rs
+++ b/openpgp/src/keyhandle.rs
@@ -134,6 +134,16 @@ impl From<KeyHandle> for KeyID {
}
}
+impl From<Option<KeyHandle>> for KeyID {
+ fn from(i: Option<KeyHandle>) -> 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 81eb23b6..e350c174 100644
--- a/openpgp/src/packet/mod.rs
+++ b/openpgp/src/packet/mod.rs
@@ -170,13 +170,16 @@ pub mod prelude;
use crate::{
crypto::{
+ Decryptor,
KeyPair,
Password,
+ SessionKey,
},
types::{
+ HashAlgorithm,
SignatureType,
+ SymmetricAlgorithm,
PublicKeyAlgorithm,
- HashAlgorithm,
},
KeyHandle,
};
@@ -478,7 +481,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,
@@ -508,7 +512,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,
@@ -1234,6 +1239,8 @@ impl<'a> std::convert::TryFrom<&'a Signature> for OnePassSig {
pub enum PKESK {
/// PKESK packet version 3.
V3(self::pkesk::PKESK3),
+ /// PKESK packet version 6.
+ V6(self::pkesk::PKESK6),
}
assert_send_and_sync!(PKESK);
@@ -1242,36 +1249,75 @@ impl PKESK {
pub fn version(&self) -> u8 {
match self {
PKESK::V3(_) => 3,
+ PKESK::V6(_) => 6,
}
}
-}
-impl From<PKESK> for Packet {
- fn from(p: PKESK) -> Self {
- Packet::PKESK(p)
+ /// Gets the recipient.
+ pub fn recipient(&self) -> Option<KeyHandle> {
+ 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(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<SymmetricAlgorithm>)
+ -> Option<(Option<SymmetricAlgorithm>, SessionKey)>
+ {
match self {
- PKESK::V3(ref mut p) => p,
+ 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<PKESK> for Packet {
+ fn from(p: PKESK) -> Self {
+ Packet::PKESK(p)
+ }
+}
+
/// Holds a symmetrically encrypted session key.
///
/// The session key is used to decrypt the actual ciphertext, which is
diff --git a/openpgp/src/packet/pkesk.rs b/openpgp/src/packet/pkesk.rs
index c394e7c8..35b90af3 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
@@ -67,34 +70,13 @@ impl PKESK3 {
where P: key::KeyParts,
R: key::KeyRole,
{
- // XXX: Corner case: for X25519 and X448 we have to prepend
- // the cipher octet to the ciphertext instead of encrypting
- // it.
-
- // We need to prefix the cipher specifier to the session key,
- // and a two-octet checksum.
- let mut psk = Vec::with_capacity(1 + session_key.len() + 2);
- psk.push(algo.into());
- psk.extend_from_slice(session_key);
-
- // XXX: Move the checksumming somewhere else.
-
- // Compute the sum modulo 65536, i.e. as u16.
- let checksum = session_key
- .iter()
- .cloned()
- .map(u16::from)
- .fold(0u16, u16::wrapping_add);
-
- psk.extend_from_slice(&checksum.to_be_bytes());
-
- let psk: SessionKey = psk.into();
- let esk = recipient.encrypt(&psk)?;
Ok(PKESK3{
common: Default::default(),
recipient: recipient.keyid(),
pk_algo: recipient.pk_algo(),
- esk,
+ esk: packet::PKESK::encrypt_common(
+ Some(algo), session_key,
+ recipient.parts_as_unspecified().role_as_unspecified())?,
})
}
@@ -155,19 +137,173 @@ impl PKESK3 {
sym_algo_hint: Option<SymmetricAlgorithm>)
-> Result<(SymmetricAlgorithm, SessionKey)>
{
- // XXX: Corner case: for X25519 and X448 we have to prepend
- // the cipher octet to the ciphertext instead of encrypting
- // it.
+ packet::PKESK::decrypt_common(&self.esk, decryptor, sym_algo_hint, true)
+ }
+}
+
+/// Returns whether the given `algo` requires checksumming, and
+/// whether the cipher octet is prepended to the encrypted session
+/// key, or it is prepended to the plain session key and then
+/// encrypted.
+fn classify_pk_algo(algo: PublicKeyAlgorithm, seipdv1: bool)
+ -> Result<(bool, bool, bool)>
+{
+ #[allow(deprecated)]
+ match algo {
+ // Classical encryption: plaintext includes the cipher
+ // octet and is checksummed.
+ PublicKeyAlgorithm::RSAEncryptSign |
+ PublicKeyAlgorithm::RSAEncrypt |
+ PublicKeyAlgorithm::ElGamalEncrypt |
+ PublicKeyAlgorithm::ElGamalEncryptSign |
+ PublicKeyAlgorithm::ECDH =>
+ Ok((true, false, seipdv1)),
+
+ // Corner case: for X25519 and X448 we have to prepend
+ // the cipher octet to the ciphertext instead of
+ // encrypting it.
+ PublicKeyAlgorithm::X25519 |
+ PublicKeyAlgorithm::X448 =>
+ Ok((false, seipdv1, false)),
+
+ a @ PublicKeyAlgorithm::RSASign |
+ a @ PublicKeyAlgorithm::DSA |
+ a @ PublicKeyAlgorithm::ECDSA |
+ a @ PublicKeyAlgorithm::EdDSA |
+ a @ PublicKeyAlgorithm::Ed25519 |
+ a @ PublicKeyAlgorithm::Ed448 |
+ a @ PublicKeyAlgorithm::Private(_) |
+ a @ PublicKeyAlgorithm::Unknown(_) =>
+ Err(Error::UnsupportedPublicKeyAlgorithm(a).into()),
+ }
+}
+
+
+impl packet::PKESK {
+ fn encrypt_common(algo: Option<SymmetricAlgorithm>,
+ session_key: &SessionKey,
+ recipient: &Key<key::UnspecifiedParts,
+ key::UnspecifiedRole>)
+ -> Result<Ciphertext>
+ {
+ let (checksummed, unencrypted_cipher_octet, encrypted_cipher_octet) =
+ classify_pk_algo(recipient.pk_algo(), algo.is_some())?;
+
+ // We may need to prefix the cipher specifier to the session
+ // key, and we may add a two-octet checksum.
+ let mut psk = Vec::with_capacity(
+ encrypted_cipher_octet.then(|| 1).unwrap_or(0)
+ + session_key.len()
+ + checksummed.then(|| 2).unwrap_or(0));
+ if let Some(algo) = algo {
+ if encrypted_cipher_octet {
+ psk.push(algo.into());
+ }
+ }
+ psk.extend_from_slice(session_key);
+
+ if checksummed {
+ // Compute the sum modulo 65536, i.e. as u16.
+ let checksum = session_key
+ .iter()
+ .cloned()
+ .map(u16::from)
+ .fold(0u16, u16::wrapping_add);
+
+ psk.extend_from_slice(&checksum.to_be_bytes());
+ }
+
+ // Make sure it is cleaned up when dropped.
+ let psk: SessionKey = psk.into();
+ let mut esk = recipient.encrypt(&psk)?;
+
+ if let Some(algo) = algo {
+ if unencrypted_cipher_octet {
+ match esk {
+ Ciphertext::X25519 { ref mut key, .. } |
+ Ciphertext::X448 { ref mut key, .. } => {
+ let mut new_key = Vec::with_capacity(1 + key.len());
+ new_key.push(algo.into());
+ new_key.extend_from_slice(key);
+ *key = new_key.into();
+ },
+ _ => unreachable!("We only prepend the cipher octet \
+ for X25519 and X448"),
+ };
+ }
+ }
+
+ Ok(esk)
+ }
+
+ fn decrypt_common(ciphertext: &Ciphertext,
+ decryptor: &mut dyn Decryptor,
+ sym_algo_hint: Option<SymmetricAlgorithm>,
+ seipdv1: bool)
+ -> Result<(SymmetricAlgorithm, SessionKey)>
+ {
+ let (checksummed, unencrypted_cipher_octet, encrypted_cipher_octet) =
+ classify_pk_algo(decryptor.public().pk_algo(), seipdv1)?;
+
+ //dbg!((checksummed, unencrypted_cipher_octet, encrypted_cipher_octet));
+
+ let mut sym_algo: Option<SymmetricAlgorithm> = None;
+ let modified_ciphertext;
+ let esk;
+ if unencrypted_cipher_octet {
+ match ciphertext {
+ Ciphertext::X25519 { e, key, } => {
+ sym_algo =
+ Some((*key.get(0).ok_or_else(
+ || Error::MalformedPacket("Short ESK".into()))?)
+ .into());
+ modified_ciphertext = Ciphertext::X25519 {
+ e: e.clone(),
+ key: key[1..].into(),
+ };
+ esk = &modified_ciphertext;
+ },
+ Ciphertext::X448 { e, key, } => {
+ sym_algo =
+ Some((*key.get(0).ok_or_else(
+ || Error::MalformedPacket("Short ESK".into()))?)
+ .into());
+ modified_ciphertext = Ciphertext::X448 {
+ e: e.clone(),
+ key: key[1..].into(),
+ };
+ esk = &modified_ciphertext;
+ },
+
+ _ => {
+ // We only prepend the cipher octet for X25519 and
+ // X448, yet we're trying to decrypt a ciphertext
+ // that uses a different algorithm, clearly
+ // something has gone wrong and will fail when we
+ // try to decrypt it downstream.
+ esk = ciphertext;
+ },
+ }
+ } else {
+ esk = ciphertext;
+ }
let plaintext_len = if let Some(s) = sym_algo_hint {
- Some(1 /* cipher octet */ + s.key_size()? + 2 /* chksum */)
+ Some(encrypted_cipher_octet.then(|| 1).unwrap_or(0)
+ + s.key_size()?
+ + checksummed.then(|| 2).unwrap_or(0))
} else {
None
};
- let plain = decryptor.decrypt(&self.esk, plaintext_len)?;
- // XXX: Move the checksumming somewhere else.
- let key_rgn = 1..plain.len().saturating_sub(2);
- let sym_algo: SymmetricAlgorithm = plain[0].into();
+ let plain = decryptor.decrypt(esk, plaintext_len)?;
+ let key_rgn = encrypted_cipher_octet.then(|| 1).unwrap_or(0)
+ ..plain.len().saturating_sub(checksummed.then(|| 2).unwrap_or(0));
+ if encrypted_cipher_octet {
+ sym_algo = Some(plain[0].into());
+ }
+ let sym_algo = sym_algo.or(sym_algo_hint)
+ .ok_or_else(|| Error::InvalidOperation(