summaryrefslogtreecommitdiffstats
path: root/openpgp/src/serialize/stream.rs
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2021-04-28 10:33:59 +0200
committerJustus Winter <justus@sequoia-pgp.org>2021-04-28 11:39:47 +0200
commitd1202ff498b234dd4da0c1ff76769e68d53bf8b4 (patch)
tree565a8523d3cdd82a7e0b4290eb2d24619cf5de35 /openpgp/src/serialize/stream.rs
parent4bc90bb02f64b59201c13567af9ce464c8243679 (diff)
openpgp: Implement Encryptor::with_session_key.
- Fixes #390.
Diffstat (limited to 'openpgp/src/serialize/stream.rs')
-rw-r--r--openpgp/src/serialize/stream.rs191
1 files changed, 186 insertions, 5 deletions
diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs
index 7b713107..6ebf1925 100644
--- a/openpgp/src/serialize/stream.rs
+++ b/openpgp/src/serialize/stream.rs
@@ -2297,6 +2297,7 @@ impl<'a> Recipient<'a> {
/// ```
pub struct Encryptor<'a> {
inner: writer::BoxStack<'a, Cookie>,
+ session_key: Option<SessionKey>,
recipients: Vec<Recipient<'a>>,
passwords: Vec<Password>,
sym_algo: SymmetricAlgorithm,
@@ -2366,8 +2367,9 @@ impl<'a> Encryptor<'a> {
where R: IntoIterator,
R::Item: Into<Recipient<'a>>,
{
- Self {
+ Self {
inner: inner.into(),
+ session_key: None,
recipients: recipients.into_iter().map(|r| r.into()).collect(),
passwords: Vec::new(),
sym_algo: Default::default(),
@@ -2409,6 +2411,7 @@ impl<'a> Encryptor<'a> {
{
Self {
inner: inner.into(),
+ session_key: None,
recipients: Vec::new(),
passwords: passwords.into_iter().map(|p| p.into()).collect(),
sym_algo: Default::default(),
@@ -2418,6 +2421,174 @@ impl<'a> Encryptor<'a> {
}
}
+ /// Creates a new encryptor for the given algorithm and session
+ /// key.
+ ///
+ /// Usually, the encryptor creates a session key and decrypts it
+ /// for the given recipients and passwords. Using this function,
+ /// the session key can be supplied instead. There are two main
+ /// use cases for this:
+ ///
+ /// - Replying to an encrypted message usually requires the
+ /// encryption (sub)keys for every recipient. If even one key
+ /// is not available, it is not possible to encrypt the new
+ /// session key. Rather than falling back to replying
+ /// unencrypted, one can reuse the original message's session
+ /// key that was encrypted for every recipient and reuse the
+ /// original [`PKESK`](crate::packet::PKESK)s.
+ ///
+ /// - Using the encryptor if the session key is transmitted or
+ /// derived using a scheme not supported by Sequoia.
+ ///
+ /// To add more passwords, use [`Encryptor::add_passwords`]. To
+ /// add recipients, use [`Encryptor::add_recipients`].
+ ///
+ /// # Examples
+ ///
+ /// This example demonstrates how to fall back to the original
+ /// message's session key in order to encrypt a reply.
+ ///
+ /// ```
+ /// # fn main() -> sequoia_openpgp::Result<()> {
+ /// # use std::io::{self, Write};
+ /// # use sequoia_openpgp as openpgp;
+ /// # use openpgp::{KeyHandle, KeyID, Fingerprint, Result};
+ /// # use openpgp::cert::prelude::*;
+ /// # use openpgp::packet::prelude::*;
+ /// # use openpgp::crypto::{KeyPair, SessionKey};
+ /// # use openpgp::types::SymmetricAlgorithm;
+ /// # use openpgp::parse::{Parse, stream::*};
+ /// # use openpgp::serialize::{Serialize, stream::*};
+ /// # use openpgp::policy::{Policy, StandardPolicy};
+ /// # let p = &StandardPolicy::new();
+ /// #
+ /// // Generate two keys.
+ /// let (alice, _) = CertBuilder::general_purpose(
+ /// None, Some("Alice Lovelace <alice@example.org>")).generate()?;
+ /// let (bob, _) = CertBuilder::general_purpose(
+ /// None, Some("Bob Babbage <bob@example.org>")).generate()?;
+ ///
+ /// // Encrypt a message for both keys.
+ /// let recipients = vec![&alice, &bob].into_iter().flat_map(|cert| {
+ /// cert.keys().with_policy(p, None).supported().alive().revoked(false)
+ /// .for_transport_encryption()
+ /// });
+ ///
+ /// let mut original = vec![];
+ /// let message = Message::new(&mut original);
+ /// let message = Encryptor::for_recipients(message, recipients).build()?;
+ /// let mut w = LiteralWriter::new(message).build()?;
+ /// w.write_all(b"Original message")?;
+ /// w.finalize()?;
+ ///
+ /// // Decrypt original message using Alice's key.
+ /// let mut decryptor = DecryptorBuilder::from_bytes(&original)?
+ /// .with_policy(p, None, Helper::new(alice))?;
+ /// io::copy(&mut decryptor, &mut io::sink())?;
+ /// let (algo, sk, pkesks) = decryptor.into_helper().recycling_bin.unwrap();
+ ///
+ /// // Compose the reply using the same session key.
+ /// let mut reply = vec![];
+ /// let mut message = Message::new(&mut reply);
+ /// 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 mut w = LiteralWriter::new(message).build()?;
+ /// w.write_all(b"Encrypted reply")?;
+ /// w.finalize()?;
+ ///
+ /// // Check that Bob can decrypt it.
+ /// let mut decryptor = DecryptorBuilder::from_bytes(&reply)?
+ /// .with_policy(p, None, Helper::new(bob))?;
+ /// io::copy(&mut decryptor, &mut io::sink())?;
+ ///
+ /// /// Decrypts the message preserving algo, session key, and PKESKs.
+ /// struct Helper {
+ /// key: Cert,
+ /// recycling_bin: Option<(SymmetricAlgorithm, SessionKey, Vec<PKESK>)>,
+ /// }
+ ///
+ /// # impl Helper {
+ /// # fn new(key: Cert) -> Self {
+ /// # Helper { key, recycling_bin: None, }
+ /// # }
+ /// # }
+ /// #
+ /// impl DecryptionHelper for Helper {
+ /// fn decrypt<D>(&mut self, pkesks: &[PKESK], _skesks: &[SKESK],
+ /// sym_algo: Option<SymmetricAlgorithm>, mut decrypt: D)
+ /// -> Result<Option<Fingerprint>>
+ /// where D: FnMut(SymmetricAlgorithm, &SessionKey) -> bool
+ /// {
+ /// let p = &StandardPolicy::new();
+ /// let mut encryption_context = None;
+ ///
+ /// 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())
+ /// .for_storage_encryption().for_transport_encryption()
+ /// {
+ /// let mut pair = ka.key().clone().into_keypair().unwrap();
+ /// if pkesk.decrypt(&mut pair, sym_algo)
+ /// .map(|(algo, session_key)| {
+ /// let success = decrypt(algo, &session_key);
+ /// if success {
+ /// // Copy algor, session key, and PKESKs.
+ /// encryption_context =
+ /// Some((algo, session_key.clone(),
+ /// pkesks.iter().cloned().collect()));
+ /// }
+ /// success
+ /// })
+ /// .unwrap_or(false)
+ /// {
+ /// break; // Decryption successful.
+ /// }
+ /// }
+ /// }
+ ///
+ /// self.recycling_bin = encryption_context; // Store for the reply.
+ /// Ok(Some(self.key.fingerprint()))
+ /// }
+ /// }
+ ///
+ /// impl VerificationHelper for Helper {
+ /// // ...
+ /// # fn get_certs(&mut self, _ids: &[KeyHandle]) -> Result<Vec<Cert>> {
+ /// # Ok(Vec::new()) // Lookup certificates here.
+ /// # }
+ /// # fn check(&mut self, structure: MessageStructure) -> Result<()> {
+ /// # Ok(()) // Implement your verification policy here.
+ /// # }
+ /// }
+ /// # Ok(()) }
+ /// ```
+ pub fn with_session_key(inner: Message<'a>,
+ sym_algo: SymmetricAlgorithm,
+ session_key: SessionKey)
+ -> Result<Self>
+ {
+ let sym_key_size = sym_algo.key_size()?;
+ if session_key.len() != sym_key_size {
+ return Err(Error::InvalidArgument(
+ format!("{} requires a {} bit key, but session key has {}",
+ sym_algo, sym_key_size, session_key.len())).into());
+ }
+
+ Ok(Self {
+ inner: inner.into(),
+ session_key: Some(session_key),
+ recipients: Vec::new(),
+ passwords: Vec::with_capacity(0),
+ sym_algo,
+ aead_algo: Default::default(),
+ hash: HashAlgorithm::SHA1.context().unwrap(),
+ cookie: Default::default(), // Will be fixed in build.
+ })
+ }
+
/// Adds recipients.
///
/// The resulting message can be encrypted by any recipient and
@@ -2691,9 +2862,12 @@ impl<'a> Encryptor<'a> {
/// # Ok(()) }
/// ```
pub fn build(mut self) -> Result<Message<'a>> {
- if self.recipients.len() + self.passwords.len() == 0 {
+ if self.recipients.len() + self.passwords.len() == 0
+ && self.session_key.is_none()
+ {
return Err(Error::InvalidOperation(
- "Neither recipients nor passwords given".into()).into());
+ "Neither recipients, passwords, nor session key given".into()
+ ).into());
}
struct AEADParameters {
@@ -2717,8 +2891,15 @@ impl<'a> Encryptor<'a> {
let mut inner = self.inner;
let level = inner.as_ref().cookie_ref().level + 1;
- // Generate a session key.
- let sk = SessionKey::new(self.sym_algo.key_size()?);
+ // Reuse existing session key or generate a new one.
+ let sym_key_size = self.sym_algo.key_size()?;
+ let sk = self.session_key.take()
+ .unwrap_or_else(|| SessionKey::new(sym_key_size));
+ if sk.len() != sym_key_size {
+ return Err(Error::InvalidOperation(
+ format!("{} requires a {} bit key, but session key has {}",
+ self.sym_algo, sym_key_size, sk.len())).into());
+ }
// Write the PKESK packet(s).
for recipient in self.recipients.iter() {