summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2020-10-22 16:29:33 +0200
committerJustus Winter <justus@sequoia-pgp.org>2020-10-22 16:43:51 +0200
commitc677bae2a7d0875c430eb641ec7601398ced9b14 (patch)
tree138ee348861d82b6a355b24186e803dad7b73bb9
parent5ff30d8ed398fc6c3e973773043c8211214db63c (diff)
openpgp: Improve documentation on how to encrypt for multiple certs.
-rw-r--r--openpgp/examples/encrypt-for.rs24
-rw-r--r--openpgp/examples/pad.rs30
-rw-r--r--openpgp/src/serialize/stream.rs61
3 files changed, 98 insertions, 17 deletions
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::<openpgp::Result<Vec<_>>>()
.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::<openpgp::Result<Vec<_>>>().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::<Vec<_>>();
+ // 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