summaryrefslogtreecommitdiffstats
path: root/openpgp/src/packet/pkesk.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/packet/pkesk.rs')
-rw-r--r--openpgp/src/packet/pkesk.rs225
1 files changed, 183 insertions, 42 deletions
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(
+ "No symmetric algorithm discovered or given".into()))?;
let mut key: SessionKey = vec![0u8; sym_algo.key_size()?].into();
if key_rgn.len() != sym_algo.key_size()? {
@@ -178,17 +314,18 @@ impl PKESK3 {
key.copy_from_slice(&plain[key_rgn]);
- let our_checksum
- = key.iter().map(|&x| x as usize).sum::<usize>() & 0xffff;
- let their_checksum = (plain[plain.len() - 2] as usize) << 8
- | (plain[plain.len() - 1] as usize);
+ if checksummed {
+ let our_checksum
+ = key.iter().map(|&x| x as usize).sum::<usize>() & 0xffff;
+ let their_checksum = (plain[plain.len() - 2] as usize) << 8
+ | (plain[plain.len() - 1] as usize);
- if their_checksum == our_checksum {
- Ok((sym_algo, key))
- } else {
- Err(Error::MalformedPacket("key checksum wrong".to_string())
- .into())
+ if their_checksum != our_checksum {
+ return Err(Error::MalformedPacket(
+ "key checksum wrong".to_string()).into());
+ }
}
+ Ok((sym_algo, key))
}
}
@@ -207,7 +344,11 @@ impl From<PKESK3> 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()
+ }
}
}