From c677bae2a7d0875c430eb641ec7601398ced9b14 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 22 Oct 2020 16:29:33 +0200 Subject: openpgp: Improve documentation on how to encrypt for multiple certs. --- openpgp/examples/encrypt-for.rs | 24 ++++++++++++---- openpgp/examples/pad.rs | 30 ++++++++++++-------- openpgp/src/serialize/stream.rs | 61 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 17 deletions(-) (limited to 'openpgp') diff --git a/openpgp/examples/encrypt-for.rs b/openpgp/examples/encrypt-for.rs index a9414a19..0b0bc998 100644 --- a/openpgp/examples/encrypt-for.rs +++ b/openpgp/examples/encrypt-for.rs @@ -40,12 +40,24 @@ fn main() -> openpgp::Result<()> { }).collect::>>() .context("Failed to read key")?; - let recipients = - certs.iter() - .flat_map(|cert| { - cert.keys() - .with_policy(p, None).alive().revoked(false).key_flags(&mode) - }); + // Build a list of recipient subkeys. + let mut recipients = Vec::new(); + for cert in certs.iter() { + // Make sure we add at least one subkey from every + // certificate. + let mut found_one = false; + for key in cert.keys().with_policy(p, None) + .supported().alive().revoked(false).key_flags(&mode) + { + recipients.push(key); + found_one = true; + } + + if ! found_one { + return Err(anyhow::anyhow!("No suitable encryption subkey for {}", + cert)); + } + } // Compose a writer stack corresponding to the output format and // packet structure we want. diff --git a/openpgp/examples/pad.rs b/openpgp/examples/pad.rs index 7977c697..b90b8982 100644 --- a/openpgp/examples/pad.rs +++ b/openpgp/examples/pad.rs @@ -8,11 +8,10 @@ use anyhow::Context; use sequoia_openpgp as openpgp; -use crate::openpgp::KeyID; use crate::openpgp::types::KeyFlags; use crate::openpgp::parse::Parse; use crate::openpgp::serialize::stream::{ - Armorer, Message, LiteralWriter, Encryptor, Recipient, padding::*, + Armorer, Message, LiteralWriter, Encryptor, padding::*, }; use crate::openpgp::policy::StandardPolicy as P; @@ -38,15 +37,24 @@ fn main() -> openpgp::Result<()> { openpgp::Cert::from_file(f) }).collect::>>().context("Failed to read key")?; - // Build a vector of recipients to hand to Encryptor. - let recipients = certs - .iter() - .flat_map(|cert| { - cert.keys() - .with_policy(p, None).alive().revoked(false).key_flags(&mode) - }) - .map(|ka| Recipient::new(KeyID::wildcard(), ka.key())) - .collect::>(); + // Build a list of recipient subkeys. + let mut recipients = Vec::new(); + for cert in certs.iter() { + // Make sure we add at least one subkey from every + // certificate. + let mut found_one = false; + for key in cert.keys().with_policy(p, None) + .supported().alive().revoked(false).key_flags(&mode) + { + recipients.push(key); + found_one = true; + } + + if ! found_one { + return Err(anyhow::anyhow!("No suitable encryption subkey for {}", + cert)); + } + } // Compose a writer stack corresponding to the output format and // packet structure we want. diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs index 0cd1902a..422e0382 100644 --- a/openpgp/src/serialize/stream.rs +++ b/openpgp/src/serialize/stream.rs @@ -2005,6 +2005,67 @@ impl<'a> Recipient<'a> { /// The stream will be encrypted using a generated session key, which /// will be encrypted using the given passwords, and for all given /// recipients. +/// +/// An [`Recipient`] is an encryption-capable (sub)key. Note that a +/// certificate may have more than one encryption-capable subkey, and +/// even the primary key may be encryption-capable. +/// +/// [`Recipient`]: struct.Recipient.html +/// +/// To encrypt for more than one certificate, iterate over the +/// certificates and select encryption-capable keys, making sure that +/// at least one key is selected from each certificate. +/// +/// # Examples +/// +/// This demonstrates encrypting for multiple certificates. +/// +/// ``` +/// # fn main() -> sequoia_openpgp::Result<()> { +/// # use std::io::Write; +/// # use sequoia_openpgp as openpgp; +/// # use openpgp::cert::prelude::*; +/// # use openpgp::parse::Parse; +/// use openpgp::serialize::stream::{ +/// Message, Encryptor, LiteralWriter, +/// }; +/// use openpgp::policy::StandardPolicy; +/// let p = &StandardPolicy::new(); +/// +/// # let (cert_0, _) = +/// # CertBuilder::general_purpose(None, Some("Mr. Pink ☮☮☮")) +/// # .generate()?; +/// # let (cert_1, _) = +/// # CertBuilder::general_purpose(None, Some("Mr. Pink ☮☮☮")) +/// # .generate()?; +/// let recipient_certs = vec![cert_0, cert_1]; +/// let mut recipients = Vec::new(); +/// for cert in recipient_certs.iter() { +/// // Make sure we add at least one subkey from every +/// // certificate. +/// let mut found_one = false; +/// for key in cert.keys().with_policy(p, None) +/// .supported().alive().revoked(false).for_transport_encryption() +/// { +/// recipients.push(key); +/// found_one = true; +/// } +/// +/// if ! found_one { +/// return Err(anyhow::anyhow!("No suitable encryption subkey for {}", +/// cert)); +/// } +/// } +/// # assert_eq!(recipients.len(), 2); +/// +/// # let mut sink = vec![]; +/// let message = Message::new(&mut sink); +/// let message = Encryptor::for_recipients(message, recipients).build()?; +/// let mut w = LiteralWriter::new(message).build()?; +/// w.write_all(b"Hello world.")?; +/// w.finalize()?; +/// # Ok(()) } +/// ``` pub struct Encryptor<'a> { // XXX: Opportunity for optimization. Previously, this writer // implemented `Drop`, so we could not move the inner writer out -- cgit v1.2.3