summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2020-05-06 18:01:47 +0200
committerJustus Winter <justus@sequoia-pgp.org>2020-05-06 18:38:19 +0200
commita74f26177e3a17974494c863d99046063c67eed2 (patch)
tree8a4e6035538443fae4f17b3a1367894391954e38
parent320884d10326fe186e974352391d51caa5cde571 (diff)
openpgp: Improve documentation of parse::stream.
- See #471.
-rw-r--r--openpgp/src/parse/stream.rs388
1 files changed, 341 insertions, 47 deletions
diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs
index fb7d0cf2..80261596 100644
--- a/openpgp/src/parse/stream.rs
+++ b/openpgp/src/parse/stream.rs
@@ -2,15 +2,14 @@
//!
//! This module provides convenient filters for decryption and
//! verification of OpenPGP messages (see [Section 11.3 of RFC 4880]).
-//! It is the preferred interface to process OpenPGP messages. These
-//! implementations use constant space.
+//! It is the preferred interface to process OpenPGP messages:
//!
//! [Section 11.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.3
//!
-//! Use the [`Verifier`] to verify a signed message,
-//! [`DetachedVerifier`] to verify a detached signature, and
-//! [`Decryptor`] to decrypt and possibly verify an encrypted and
-//! possibly signed message.
+//! - Use the [`Verifier`] to verify a signed message,
+//! - [`DetachedVerifier`] to verify a detached signature,
+//! - or [`Decryptor`] to decrypt and verify an encrypted and
+//! possibly signed message.
//!
//! [`Verifier`]: struct.Verifier.html
//! [`DetachedVerifier`]: struct.DetachedVerifier.html
@@ -32,21 +31,46 @@
//! The [`VerificationHelper`] trait give certificates for the
//! signature verification to the [`Verifier`] or [`Decryptor`], let
//! you inspect the message structure (see [Section 11.3 of RFC
-//! 4880]).
+//! 4880]), and implements the signature verification policy.
//!
-//! public and for the signature verification, and implements the
-//! signature verification policy.
+//! The [`DecryptionHelper`] trait is concerned with producing the
+//! session key to decrypt a message, most commonly by decrypting one
+//! of the messages' [`PKESK`] or [`SKESK`] packets. It could also
+//! use a cached session key, or one that has been explicitly provided
+//! to the decryption operation.
//!
-//! To decrypt messages, we create a [`Verifier`] with our helper.
-//! Verified data can be read from this using [`io::Read`].
+//! [`PKESK`]: ../../packet/enum.PKESK.html
+//! [`SKESK`]: ../../packet/enum.SKESK.html
+//!
+//! The [`Verifier`] and [`Decryptor`] are filters: they consume
+//! OpenPGP data from a reader, file, or bytes, and implement
+//! [`io::Read`] that can be used to read the verified and/or
+//! decrypted data.
+//!
+//! [`io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
+//!
+//! [`DetachedVerifier`] does not provide the [`io::Read`] interface,
+//! because in this case, the data to be verified is easily available
+//! without any transformation. Not providing a filter-like interface
+//! allows for a very performant implementation of the verification.
//!
//! # Examples
//!
+//! This example demonstrates how to use the streaming interface using
+//! the [`Verifier`]. For brevity, no certificates are fed to the
+//! verifier, and the message structure is not verified, i.e. this
+//! merely extracts the literal data. See the [`Verifier` examples]
+//! and the [`Decryptor` examples] for how to verify the message and
+//! its structure.
+//!
+//! [`Verifier` examples]: struct.Verifier.html#examples
+//! [`Decryptor` examples]: struct.Decryptor.html#examples
+//!
//! ```
-//! # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> {
+//! # fn main() -> sequoia_openpgp::Result<()> {
//! use std::io::Read;
//! use sequoia_openpgp as openpgp;
-//! use openpgp::{KeyID, Cert, Result};
+//! use openpgp::{KeyHandle, Cert, Result};
//! use openpgp::parse::stream::*;
//! use openpgp::policy::StandardPolicy;
//!
@@ -55,7 +79,7 @@
//! // This fetches keys and computes the validity of the verification.
//! struct Helper {};
//! impl VerificationHelper for Helper {
-//! fn get_certs(&mut self, _ids: &[openpgp::KeyHandle]) -> Result<Vec<Cert>> {
+//! fn get_certs(&mut self, _ids: &[KeyHandle]) -> Result<Vec<Cert>> {
//! Ok(Vec::new()) // Feed the Certs to the verifier here...
//! }
//! fn check(&mut self, structure: MessageStructure) -> Result<()> {
@@ -80,7 +104,7 @@
//! v.read_to_end(&mut content)?;
//! assert_eq!(content, b"Hello World!");
//! # Ok(()) }
-
+//! ```
use std::cmp;
use std::io::{self, Read};
use std::path::Path;
@@ -135,7 +159,9 @@ pub type VerificationResult<'a> =
/// - The signature has a Signature Creation Time subpacket.
///
/// - The signature is alive at the specified time (the time
-/// parameter passed to, e.g., `Verifier::from_reader`).
+/// parameter passed to, e.g., [`Verifier::from_reader`]).
+///
+/// [`Verifier::from_reader`]: struct.Verifier.html#method.from_reader
///
/// - The certificate is alive and not revoked as of the signature's
/// creation time.
@@ -150,10 +176,11 @@ pub type VerificationResult<'a> =
/// belongs to the person or entity that the user thinks it
/// belongs to. This property can only be evaluated within a
/// trust model, such as the [web of trust] (WoT). This policy is
-/// normally implemented in the `VerificationHelper::check`
+/// normally implemented in the [`VerificationHelper::check`]
/// method.
///
-/// [web of trust]: https://en.wikipedia.org/wiki/Web_of_trust
+/// [web of trust]: https://en.wikipedia.org/wiki/Web_of_trust
+/// [`VerificationHelper::check`]: trait.VerificationHelper.html#tymethod.check
#[derive(Debug)]
pub struct GoodChecksum<'a> {
/// The signature.
@@ -463,21 +490,44 @@ enum IMessageLayer {
}
/// Helper for signature verification.
+///
+/// This trait abstracts over signature and message structure
+/// verification. It allows us to provide the [`Verifier`],
+/// [`DetachedVerifier`], and [`Decryptor`] without imposing a policy
+/// on how certificates for signature verification are looked up, or
+/// what message structure is considered acceptable.
+///
+/// [`Verifier`]: struct.Verifier.html
+/// [`DetachedVerifier`]: struct.DetachedVerifier.html
+/// [`Decryptor`]: struct.Decryptor.html
+///
+/// It also allows you to inspect each packet that is processed during
+/// verification or decryption, optionally providing a [`Map`] for
+/// each packet.
+///
+/// [`Map`]: ../map/struct.Map.html
pub trait VerificationHelper {
/// Turns mapping on or off.
///
/// If this function returns true, the packet parser will create a
- /// map of the packets. Note that this buffers the packets
+ /// [`Map`] of the packets. Note that this buffers the packets
/// contents, and is not recommended unless you know that the
- /// packets are small. The default implementation returns false.
+ /// packets are small. This is called once before parsing the
+ /// first packet.
+ ///
+ /// [`Map`]: ../map/struct.Map.html
+ ///
+ /// The default implementation returns false.
fn mapping(&self) -> bool {
false
}
/// Inspects the message.
///
- /// Called once per packet. Can be used to dump packets in
- /// encrypted messages. The default implementation does nothing.
+ /// Called once per packet. Can be used to inspect and dump
+ /// packets in encrypted messages.
+ ///
+ /// The default implementation does nothing.
fn inspect(&mut self, pp: &PacketParser) -> Result<()> {
// Do nothing.
let _ = pp;
@@ -500,6 +550,36 @@ pub trait VerificationHelper {
/// a subset of them may be sufficient.
///
/// This method will be called at most once per message.
+ ///
+ /// # Examples
+ ///
+ /// This example demonstrates how to look up the certificates for
+ /// the signature verification given the list of signature
+ /// issuers.
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// use openpgp::{KeyHandle, Cert, Result};
+ /// use openpgp::parse::stream::*;
+ /// # fn lookup_cert_by_handle(_: &KeyHandle) -> Result<Cert> {
+ /// # unimplemented!()
+ /// # }
+ ///
+ /// struct Helper { /* ... */ };
+ /// impl VerificationHelper for Helper {
+ /// fn get_certs(&mut self, ids: &[KeyHandle]) -> Result<Vec<Cert>> {
+ /// let mut certs = Vec::new();
+ /// for id in ids {
+ /// certs.push(lookup_cert_by_handle(id)?);
+ /// }
+ /// Ok(certs)
+ /// }
+ /// // ...
+ /// # fn check(&mut self, structure: MessageStructure) -> Result<()> {
+ /// # unimplemented!()
+ /// # }
+ /// }
+ /// ```
fn get_certs(&mut self, ids: &[crate::KeyHandle]) -> Result<Vec<Cert>>;
/// Conveys the message structure.
@@ -520,7 +600,46 @@ pub trait VerificationHelper {
/// such, any error returned by this function will abort reading,
/// and the error will be propagated via the `io::Read` operation.
///
- /// This method will be called at most once per message.
+ /// This method will be called exactly once per message.
+ ///
+ /// # Examples
+ ///
+ /// This example demonstrates how to verify that the message is an
+ /// encrypted, optionally compressed, and signed message that has
+ /// at least one valid signature.
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// use openpgp::{KeyHandle, Cert, Result};
+ /// use openpgp::parse::stream::*;
+ ///
+ /// struct Helper { /* ... */ };
+ /// impl VerificationHelper for Helper {
+ /// # fn get_certs(&mut self, ids: &[KeyHandle]) -> Result<Vec<Cert>> {
+ /// # unimplemented!();
+ /// # }
+ /// fn check(&mut self, structure: MessageStructure) -> Result<()> {
+ /// for (i, layer) in structure.into_iter().enumerate() {
+ /// match layer {
+ /// MessageLayer::Encryption { .. } if i == 0 => (),
+ /// MessageLayer::Compression { .. } if i == 1 => (),
+ /// MessageLayer::SignatureGroup { ref results }
+ /// if i == 1 || i == 2 =>
+ /// {
+ /// if ! results.iter().any(|r| r.is_ok()) {
+ /// return Err(anyhow::anyhow!(
+ /// "No valid signature"));
+ /// }
+ /// }
+ /// _ => return Err(anyhow::anyhow!(
+ /// "Unexpected message structure")),
+ /// }
+ /// }
+ /// Ok(())
+ /// }
+ /// // ...
+ /// }
+ /// ```
fn check(&mut self, structure: MessageStructure) -> Result<()>;
}
@@ -573,35 +692,72 @@ impl<V: VerificationHelper> DecryptionHelper for NoDecryptionHelper<V> {
/// # Examples
///
/// ```
-/// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> {
+/// # fn main() -> sequoia_openpgp::Result<()> {
/// use std::io::Read;
/// use sequoia_openpgp as openpgp;
-/// use openpgp::{KeyID, Cert, Result};
+/// use openpgp::{KeyHandle, Cert, Result};
/// use openpgp::parse::stream::*;
/// use openpgp::policy::StandardPolicy;
+/// # use sequoia_openpgp::parse::Parse;
+/// # fn lookup_cert_by_handle(_: &KeyHandle) -> Result<Cert> {
+/// # Cert::from_bytes(
+/// # &b"-----BEGIN PGP PUBLIC KEY BLOCK-----
+/// #
+/// # xjMEWlNvABYJKwYBBAHaRw8BAQdA+EC2pvebpEbzPA9YplVgVXzkIG5eK+7wEAez
+/// # lcBgLJrNMVRlc3R5IE1jVGVzdGZhY2UgKG15IG5ldyBrZXkpIDx0ZXN0eUBleGFt
+/// # cGxlLm9yZz7CkAQTFggAOBYhBDnRAKtn1b2MBAECBfs3UfFYfa7xBQJaU28AAhsD
+/// # BQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEPs3UfFYfa7xJHQBAO4/GABMWUcJ
+/// # 5D/DZ9b+6YiFnysSjCT/gILJgxMgl7uoAPwJherI1pAAh49RnPHBR1IkWDtwzX65
+/// # CJG8sDyO2FhzDs44BFpTbwASCisGAQQBl1UBBQEBB0B+A0GRHuBgdDX50T1nePjb
+/// # mKQ5PeqXJbWEtVrUtVJaPwMBCAfCeAQYFggAIBYhBDnRAKtn1b2MBAECBfs3UfFY
+/// # fa7xBQJaU28AAhsMAAoJEPs3UfFYfa7xzjIBANX2/FgDX3WkmvwpEHg/sn40zACM
+/// # W2hrBY5x0sZ8H7JlAP47mCfCuRVBqyaePuzKbxLJeLe2BpDdc0n2izMVj8t9Cg==
+/// # =QetZ
+/// # -----END PGP PUBLIC KEY BLOCK-----"[..])
+/// # }
///
/// let p = &StandardPolicy::new();
///
/// // This fetches keys and computes the validity of the verification.
/// struct Helper {};
/// impl VerificationHelper for Helper {
-/// fn get_certs(&mut self, _ids: &[openpgp::KeyHandle]) -> Result<Vec<Cert>> {
-/// Ok(Vec::new()) // Feed the Certs to the verifier here...
+/// fn get_certs(&mut self, ids: &[KeyHandle]) -> Result<Vec<Cert>> {
+/// let mut certs = Vec::new();
+/// for id in ids {
+/// certs.push(lookup_cert_by_handle(id)?);
+/// }
+/// Ok(certs)
/// }
+///
/// fn check(&mut self, structure: MessageStructure) -> Result<()> {
-/// Ok(()) // Implement your verification policy here.
+/// for (i, layer) in structure.into_iter().enumerate() {
+/// match layer {
+/// MessageLayer::Encryption { .. } if i == 0 => (),
+/// MessageLayer::Compression { .. } if i == 1 => (),
+/// MessageLayer::SignatureGroup { ref results } => {
+/// if ! results.iter().any(|r| r.is_ok()) {
+/// return Err(anyhow::anyhow!(
+/// "No valid signature"));
+/// }
+/// }
+/// _ => return Err(anyhow::anyhow!(
+/// "Unexpected message structure")),
+/// }
+/// }
+/// Ok(())
/// }
/// }
///
/// let message =
/// b"-----BEGIN PGP MESSAGE-----
///
-/// xA0DAAoWBpwMNI3YLBkByxJiAAAAAABIZWxsbyBXb3JsZCHCdQQAFgoAJwWCW37P
-/// 8RahBI6MM/pGJjN5dtl5eAacDDSN2CwZCZAGnAw0jdgsGQAAeZQA/2amPbBXT96Q
-/// O7PFms9DRuehsVVrFkaDtjN2WSxI4RGvAQDq/pzNdCMpy/Yo7AZNqZv5qNMtDdhE
-/// b2WH5lghfKe/AQ==
-/// =DjuO
-/// -----END PGP MESSAGE-----";
+/// xA0DAAoW+zdR8Vh9rvEByxJiAAAAAABIZWxsbyBXb3JsZCHCdQQAFgoABgWCXrLl
+/// AQAhCRD7N1HxWH2u8RYhBDnRAKtn1b2MBAECBfs3UfFYfa7xRUsBAJaxkU/RCstf
+/// UD7TM30IorO1Mb9cDa/hPRxyzipulT55AQDN1m9LMqi9yJDjHNHwYYVwxDcg+pLY
+/// YmAFv/UfO0vYBw==
+/// =+l94
+/// -----END PGP MESSAGE-----
+/// ";
///
/// let h = Helper {};
/// let mut v = Verifier::from_bytes(p, message, h, None)?;
@@ -683,9 +839,12 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> {
self.decryptor.into_helper().v
}
- /// Returns true if the whole message has been processed and the verification result is ready.
- /// If the function returns false the message did not fit into the internal buffer and
- /// **unverified** data must be `read()` from the instance until EOF.
+ /// Returns true if the whole message has been processed and the
+ /// verification result is ready.
+ ///
+ /// If the function returns false the message did not fit into the
+ /// internal buffer and **unverified** data must be `read()` from
+ /// the instance until EOF.
pub fn message_processed(&self) -> bool {
// oppr is only None after we've processed the packet sequence.
self.decryptor.message_processed()
@@ -730,10 +889,10 @@ impl<'a, H: VerificationHelper> io::Read for Verifier<'a, H> {
/// # Examples
///
/// ```
-/// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> {
+/// # fn main() -> sequoia_openpgp::Result<()> {
/// use std::io::{self, Read};
/// use sequoia_openpgp as openpgp;
-/// use openpgp::{KeyID, Cert, Result};
+/// use openpgp::{KeyHandle, Cert, Result};
/// use openpgp::parse::stream::*;
/// use sequoia_openpgp::policy::StandardPolicy;
///
@@ -742,7 +901,7 @@ impl<'a, H: VerificationHelper> io::Read for Verifier<'a, H> {
/// // This fetches keys and computes the validity of the verification.
/// struct Helper {};
/// impl VerificationHelper for Helper {
-/// fn get_certs(&mut self, _ids: &[openpgp::KeyHandle]) -> Result<Vec<Cert>> {
+/// fn get_certs(&mut self, _ids: &[KeyHandle]) -> Result<Vec<Cert>> {
/// Ok(Vec::new()) // Feed the Certs to the verifier here...
/// }
/// fn check(&mut self, structure: MessageStructure) -> Result<()> {
@@ -918,7 +1077,7 @@ enum Mode {
/// # Examples
///
/// ```
-/// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> {
+/// # fn main() -> sequoia_openpgp::Result<()> {
/// use std::io::Read;
/// use sequoia_openpgp as openpgp;
/// use openpgp::crypto::SessionKey;
@@ -1011,17 +1170,152 @@ pub struct Decryptor<'a, H: VerificationHelper + DecryptionHelper> {
}
/// Helper for decrypting messages.
+///
+/// This trait abstracts over session key decryption. It allows us to
+/// provide the [`Decryptor`] without imposing any policy on how the
+/// session key is decrypted.
+///
+/// [`Decryptor`]: struct.Decryptor.html
pub trait DecryptionHelper {
/// Decrypts the message.
///
- /// This function is called with every `PKESK` and `SKESK` found
- /// in the message. The implementation must decrypt the symmetric
- /// algorithm and session key from one of the PKESK packets, the
- /// SKESKs, or retrieve it from a cache, and then call `decrypt`
- /// with the symmetric algorithm and session key.
+ /// This function is called with every [`PKESK`] and [`SKESK`]
+ /// packet found in the message. The implementation must decrypt
+ /// the symmetric algorithm and session key from one of the
+ /// [`PKESK`] packets, the [`SKESK`] packets, or retrieve it from
+ /// a cache, and then call `decrypt` with the symmetric algorithm
+ /// and session key.
+ ///
+ /// [`PKESK`]: ../../packet/enum.PKESK.html
+ /// [`SKESK`]: ../../packet/enum.SKESK.html
///
/// If a symmetric algorithm is given, it should be passed on to
- /// PKESK::decrypt.
+ /// [`PKESK::decrypt`].
+ ///
+ /// [`PKESK::decrypt`]: ../../packet/enum.PKESK.html#method.decrypt
+ ///
+ /// If the message is decrypted using a [`PKESK`] packet, then the
+ /// fingerprint of the certificate containing the encryption
+ /// subkey should be returned. This is used in conjunction with
+ /// the intended recipient subpacket (see [Section 5.2.3.29 of RFC
+ /// 4880bis]) to prevent [*Surreptitious Forwarding*].
+ ///
+ /// [Section 5.2.3.29 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.2.3.29
+ /// [*Surreptitious Forwarding*]: http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html
+ ///
+ /// This method will be called once per encryption layer.
+ ///
+ /// # Examples
+ ///
+ /// This example demonstrates how to decrypt a message using local
+ /// keys (i.e. excluding remote keys like smart cards) while
+ /// maximizing convenience for the user.
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// use openpgp::{Fingerprint, Cert, Result};
+ /// # use openpgp::KeyID;
+ /// use openpgp::crypto::SessionKey;
+ /// use openpgp::types::SymmetricAlgorithm;
+ /// use openpgp::packet::{PKESK, SKESK};
+ /// # use openpgp::packet::{Key, key::*};
+ /// use openpgp::parse::stream::*;
+ /// # fn lookup_cache(_: &[PKESK], _: &[SKESK])
+ /// # -> Option<(Option<Fingerprint>, SymmetricAlgorithm, SessionKey)> {
+ /// # unimplemented!()
+ /// # }
+ /// # fn lookup_key(_: &KeyID)
+ /// # -> Option<(Fingerprint, Key<SecretParts, UnspecifiedRole>)> {
+ /// # unimplemented!()
+ /// # }
+ /// # fn all_keys() -> impl Iterator<Item = (Fingerprint, Key<SecretParts, UnspecifiedRole>)> {
+ /// # Vec::new().into_iter()
+ /// # }
+ ///
+ /// struct Helper { /* ... */ };
+ /// 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) -> Result<()>
+ /// {
+ /// // Try to decrypt, from the most convenient method to the
+ /// // least convenient one.
+ ///
+ /// // First, see if it is in the cache.
+ /// if let Some((fp, algo, sk)) = lookup_cache(pkesks, skesks) {
+ /// if decrypt(algo, &sk).is_ok() {
+ /// return Ok(fp);
+ /// }
+ /// }
+ ///
+ /// // Second, we try those keys that we can use without
+ /// // prompting for a password.
+ /// for pkesk in pkesks {
+ /// if let Some((fp, key)) = lookup_key(pkesk.recipient()) {
+ /// if ! key.secret().is_encrypted() {
+ /// let mut keypair = key.clone().into_keypair()?;
+ /// if pkesk.decrypt(&mut keypair, sym_algo)
+ /// .and_then(|(algo, sk)| decrypt(algo, &sk))
+ /// .is_ok()
+ /// {
+ /// return Ok(Some(fp));
+ /// }
+ /// }
+ /// }
+ /// }
+ ///
+ /// // Third, we try to decrypt PKESK packets with
+ /// // wildcard recipients using those keys that we can
+ /// // use without prompting for a password.
+ /// for pkesk in pkesks.iter().filter(|p| p.recipient().is_wildcard()) {
+ /// for (fp, key) in all_keys() {
+ /// if ! key.secret().is_encrypted() {
+ /// let mut keypair = key.clone().into_keypair()?;
+ /// if pkesk.decrypt(&mut keypair, sym_algo)
+ /// .and_then(|(algo, sk)| decrypt(algo, &sk))
+ /// .is_ok()
+ /// {
+ /// return Ok(Some(fp));
+ /// }
+ /// }
+ /// }
+ /// }
+ ///
+ /// // Fourth, we try to decrypt all PKESK packets that we
+ /// // need encrypted keys for.
+ /// // [...]
+ ///
+ /// // Fifth, we try to decrypt all PKESK packets with
+ /// // wildcard recipients using encrypted keys.
+ /// // [...]
+ ///
+ /// // At this point, we have exhausted our options at
+ /// // decrypting the PKESK packets.
+ /// if skesks.is_empty() {
+ /// return
+ /// Err(anyhow::anyhow!("No key to decrypt message"));
+ /// }
+ ///
+ /// // Finally, try to decrypt using the SKESKs.
+ /// loop {
+ /// let password = // Prompt for a password.
+ /// # "".into();
+ ///
+ /// for skesk in skesks {
+ /// if skesk.decrypt(&password)
+ /// .and_then(|(algo, sk)| decrypt(algo, &sk))
+ /// .is_ok()
+ /// {
+ /// return Ok(None);
+ /// }
+ /// }
+ ///
+ /// eprintln!("Bad password.");
+ /// }
+ /// }
+ /// }
+ /// ```
fn decrypt<D>(&mut self, pkesks: &[PKESK], skesks: &[SKESK],
sym_algo: Option<SymmetricAlgorithm>,
decrypt: D) -> Result<Option<Fingerprint>>