diff options
Diffstat (limited to 'sop/src')
-rw-r--r-- | sop/src/cli.rs | 334 | ||||
-rw-r--r-- | sop/src/dates.rs | 95 | ||||
-rw-r--r-- | sop/src/errors.rs | 101 | ||||
-rw-r--r-- | sop/src/main.rs | 786 |
4 files changed, 0 insertions, 1316 deletions
diff --git a/sop/src/cli.rs b/sop/src/cli.rs deleted file mode 100644 index 202dd5dd..00000000 --- a/sop/src/cli.rs +++ /dev/null @@ -1,334 +0,0 @@ -use std::fmt; -use std::path::Path; - -use anyhow::Context; -use chrono::{DateTime, offset::Utc}; -use structopt::StructOpt; - -use sequoia_openpgp as openpgp; -use openpgp::{ - cert::{ - Cert, - CertParser, - }, - crypto::{ - Password, - }, - types::{ - SignatureType, - DataFormat, - }, - parse::Parse, -}; - -use super::{ - dates, - Error, - Result, -}; - -#[derive(StructOpt)] -#[structopt(about = "An implementation of the \ - Stateless OpenPGP Command Line Interface \ - using Sequoia")] -pub enum SOP { - /// Prints version information. - Version { - }, - /// Generates a Secret Key. - GenerateKey { - /// Don't ASCII-armor output. - #[structopt(long)] - no_armor: bool, - /// UserIDs for the generated key. - userids: Vec<String>, - }, - /// Extracts a Certificate from a Secret Key. - ExtractCert { - /// Don't ASCII-armor output. - #[structopt(long)] - no_armor: bool, - }, - /// Creates Detached Signatures. - Sign { - /// Don't ASCII-armor output. - #[structopt(long)] - no_armor: bool, - /// Sign binary data or UTF-8 text. - #[structopt(default_value = "binary", long = "as")] - as_: SignAs, - /// Keys for signing. - keys: Vec<String>, - }, - /// Verifies Detached Signatures. - Verify { - /// Consider signatures before this date invalid. - #[structopt(long, parse(try_from_str = dates::parse_bound_round_down))] - not_before: Option<DateTime<Utc>>, - /// Consider signatures after this date invalid. - #[structopt(long, parse(try_from_str = dates::parse_bound_round_up))] - not_after: Option<DateTime<Utc>>, - /// Signatures to verify. - signatures: String, - /// Certs for verification. - certs: Vec<String>, - }, - /// Encrypts a Message. - Encrypt { - /// Don't ASCII-armor output. - #[structopt(long)] - no_armor: bool, - /// Encrypt binary data, UTF-8 text, or MIME data. - #[structopt(default_value = "binary", long = "as")] - as_: EncryptAs, - /// Encrypt with passwords. - #[structopt(long)] - with_password: Vec<String>, - /// Keys for signing. - #[structopt(long)] - sign_with: Vec<String>, - /// Encrypt for these certs. - certs: Vec<String>, - }, - /// Decrypts a Message. - Decrypt { - /// Write the session key here. - #[structopt(long)] - session_key_out: Option<String>, - /// Try to decrypt with this session key. - #[structopt(long)] - with_session_key: Vec<String>, - /// Try to decrypt with this password. - #[structopt(long)] - with_password: Vec<String>, - /// Write verification result here. - #[structopt(long)] - verify_out: Option<String>, - /// Certs for verification. - #[structopt(long)] - verify_with: Vec<String>, - /// Consider signatures before this date invalid. - #[structopt(long, parse(try_from_str = dates::parse_bound_round_down))] - verify_not_before: Option<DateTime<Utc>>, - /// Consider signatures after this date invalid. - #[structopt(long, parse(try_from_str = dates::parse_bound_round_up))] - verify_not_after: Option<DateTime<Utc>>, - /// Try to decrypt with this key. - key: Vec<String>, - }, - /// Converts binary OpenPGP data to ASCII - Armor { - /// Indicates the kind of data - #[structopt(long, default_value = "auto")] - label: ArmorKind, - }, - /// Converts ASCII OpenPGP data to binary - Dearmor { - }, - /// Unsupported subcommand. - #[structopt(external_subcommand)] - Unsupported(Vec<String>), -} - -#[derive(Clone, Copy)] -pub enum SignAs { - Binary, - Text, -} - -impl std::str::FromStr for SignAs { - type Err = anyhow::Error; - fn from_str(s: &str) -> openpgp::Result<Self> { - match s { - "binary" => Ok(SignAs::Binary), - "text" => Ok(SignAs::Text), - _ => Err(anyhow::anyhow!( - "{:?}, expected one of {{binary|text}}", s)), - } - } -} - -impl fmt::Display for SignAs { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - SignAs::Binary => f.write_str("binary"), - SignAs::Text => f.write_str("text"), - } - } -} - -impl From<SignAs> for SignatureType { - fn from(a: SignAs) -> Self { - match a { - SignAs::Binary => SignatureType::Binary, - SignAs::Text => SignatureType::Text, - } - } -} - -#[derive(Clone, Copy)] -pub enum EncryptAs { - Binary, - Text, - MIME, -} - -impl std::str::FromStr for EncryptAs { - type Err = anyhow::Error; - fn from_str(s: &str) -> openpgp::Result<Self> { - match s { - "binary" => Ok(EncryptAs::Binary), - "text" => Ok(EncryptAs::Text), - "mime" => Ok(EncryptAs::MIME), - _ => Err(anyhow::anyhow!( - "{}, expected one of {{binary|text|mime}}", s)), - } - } -} - -impl fmt::Display for EncryptAs { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - EncryptAs::Binary => f.write_str("binary"), - EncryptAs::Text => f.write_str("text"), - EncryptAs::MIME => f.write_str("mime"), - } - } -} - -impl From<EncryptAs> for SignatureType { - fn from(a: EncryptAs) -> Self { - match a { - EncryptAs::Binary => SignatureType::Binary, - EncryptAs::Text => SignatureType::Text, - // XXX: We should inspect the serialized MIME structure - // and use Text if it is UTF-8, Binary otherwise. But, we - // cannot be bothered at this point. - EncryptAs::MIME => SignatureType::Binary, - } - } -} - -impl From<EncryptAs> for DataFormat { - fn from(a: EncryptAs) -> Self { - match a { - EncryptAs::Binary => DataFormat::Binary, - EncryptAs::Text => DataFormat::Text, - EncryptAs::MIME => DataFormat::MIME, - } - } -} - -#[derive(Clone, Copy)] -pub enum ArmorKind { - Auto, - Sig, - Key, - Cert, - Message, -} - -impl std::str::FromStr for ArmorKind { - type Err = anyhow::Error; - fn from_str(s: &str) -> openpgp::Result<Self> { - match s { - "auto" => Ok(ArmorKind::Auto), - "sig" => Ok(ArmorKind::Sig), - "key" => Ok(ArmorKind::Key), - "cert" => Ok(ArmorKind::Cert), - "message" => Ok(ArmorKind::Message), - _ => Err(anyhow::anyhow!( - "{:?}, expected one of \ - {{auto|sig|key|cert|message}}", s)), - } - } -} - -impl fmt::Display for ArmorKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ArmorKind::Auto => f.write_str("auto"), - ArmorKind::Sig => f.write_str("sig"), - ArmorKind::Key => f.write_str("key"), - ArmorKind::Cert => f.write_str("cert"), - ArmorKind::Message => f.write_str("message"), - } - } -} - - -fn is_special_designator<S: AsRef<str>>(file: S) -> bool { - file.as_ref().starts_with("@") -} - -/// Loads the given (special) file. -pub fn load_file<S: AsRef<str>>(file: S) -> Result<std::fs::File> { - let f = file.as_ref(); - - if is_special_designator(f) { - if Path::new(f).exists() { - return Err(anyhow::Error::from(Error::AmbiguousInput)) - .context(format!("File {:?} exists", f)); - } - - return Err(anyhow::Error::from(Error::UnsupportedSpecialPrefix)); - } - - std::fs::File::open(f).map_err(|_| Error::MissingInput) - .context(format!("Failed to open file {:?}", f)) -} - -/// Creates the given (special) file. -pub fn create_file<S: AsRef<str>>(file: S) -> Result<std::fs::File> { - let f = file.as_ref(); - - if is_special_designator(f) { - if Path::new(f).exists() { - return Err(anyhow::Error::from(Error::AmbiguousInput)) - .context(format!("File {:?} exists", f)); - } - - return Err(anyhow::Error::from(Error::UnsupportedSpecialPrefix)); - } - - if Path::new(f).exists() { - return Err(anyhow::Error::from(Error::OutputExists)) - .context(format!("File {:?} exists", f)); - } - - std::fs::File::create(f).map_err(|_| Error::MissingInput) // XXX - .context(format!("Failed to create file {:?}", f)) -} - -/// Loads the certs given by the (special) files. -pub fn load_certs(files: Vec<String>) -> Result<Vec<Cert>> { - let mut certs = vec![]; - for f in files { - let r = load_file(&f)?; - for cert in CertParser::from_reader(r).map_err(|_| Error::BadData) - .context(format!("Failed to load CERTS from file {:?}", f))? - { - certs.push( - cert.context(format!("Malformed certificate in file {:?}", f))? - ); - } - } - Ok(certs) -} - -/// Loads the KEY given by the (special) files. -pub fn load_keys(files: Vec<String>) -> Result<Vec<Cert>> { - let mut keys = vec![]; - for f in files { - let r = load_file(&f)?; - keys.push(Cert::from_reader(r).map_err(|_| Error::BadData) - .context(format!("Failed to load KEY from file {:?}", f))?); - } - Ok(keys) -} - -/// Frobnicates the strings and converts them to passwords. -pub fn frob_passwords(p: Vec<String>) -> Result<Vec<Password>> { - // XXX: Maybe do additional checks. - Ok(p.iter().map(|p| p.trim_end().into()).collect()) -} diff --git a/sop/src/dates.rs b/sop/src/dates.rs deleted file mode 100644 index b76fe1fc..00000000 --- a/sop/src/dates.rs +++ /dev/null @@ -1,95 +0,0 @@ -use chrono::{DateTime, offset::Utc}; - -use crate::Result; - -/// Parses the given string depicting a ISO 8601 timestamp, rounding down. -pub fn parse_bound_round_down(s: &str) -> Result<DateTime<Utc>> { - match s { - // XXX: parse "-" to None once we figure out how to do that - // with structopt. - "now" => Ok(Utc::now()), - _ => parse_iso8601(s, chrono::NaiveTime::from_hms(0, 0, 0)), - } -} - -/// Parses the given string depicting a ISO 8601 timestamp, rounding up. -pub fn parse_bound_round_up(s: &str) -> Result<DateTime<Utc>> { - match s { - // XXX: parse "-" to None once we figure out how to do that - // with structopt. - "now" => Ok(Utc::now()), - _ => parse_iso8601(s, chrono::NaiveTime::from_hms(23, 59, 59)), - } -} - -/// Parses the given string depicting a ISO 8601 timestamp. -fn parse_iso8601(s: &str, pad_date_with: chrono::NaiveTime) - -> Result<DateTime<Utc>> -{ - // If you modify this function this function, synchronize the - // changes with the copy in sqv.rs! - for f in &[ - "%Y-%m-%dT%H:%M:%S%#z", - "%Y-%m-%dT%H:%M:%S", - "%Y-%m-%dT%H:%M%#z", - "%Y-%m-%dT%H:%M", - "%Y-%m-%dT%H%#z", - "%Y-%m-%dT%H", - "%Y%m%dT%H%M%S%#z", - "%Y%m%dT%H%M%S", - "%Y%m%dT%H%M%#z", - "%Y%m%dT%H%M", - "%Y%m%dT%H%#z", - "%Y%m%dT%H", - ] { - if f.ends_with("%#z") { - if let Ok(d) = DateTime::parse_from_str(s, *f) { - return Ok(d.into()); - } - } else { - if let Ok(d) = chrono::NaiveDateTime::parse_from_str(s, *f) { - return Ok(DateTime::from_utc(d, Utc)); - } - } - } - for f in &[ - "%Y-%m-%d", - "%Y-%m", - "%Y-%j", - "%Y%m%d", - "%Y%m", - "%Y%j", - "%Y", - ] { - if let Ok(d) = chrono::NaiveDate::parse_from_str(s, *f) { - return Ok(DateTime::from_utc(d.and_time(pad_date_with), Utc)); - } - } - Err(anyhow::anyhow!("Malformed ISO8601 timestamp: {}", s)) -} - -#[test] -fn test_parse_iso8601() { - let z = chrono::NaiveTime::from_hms(0, 0, 0); - parse_iso8601("2017-03-04T13:25:35Z", z).unwrap(); - parse_iso8601("2017-03-04T13:25:35+08:30", z).unwrap(); - parse_iso8601("2017-03-04T13:25:35", z).unwrap(); - parse_iso8601("2017-03-04T13:25Z", z).unwrap(); - parse_iso8601("2017-03-04T13:25", z).unwrap(); - // parse_iso8601("2017-03-04T13Z", z).unwrap(); // XXX: chrono doesn't like - // parse_iso8601("2017-03-04T13", z).unwrap(); // ditto - parse_iso8601("2017-03-04", z).unwrap(); - // parse_iso8601("2017-03", z).unwrap(); // ditto - parse_iso8601("2017-031", z).unwrap(); - parse_iso8601("20170304T132535Z", z).unwrap(); - parse_iso8601("20170304T132535+0830", z).unwrap(); - parse_iso8601("20170304T132535", z).unwrap(); - parse_iso8601("20170304T1325Z", z).unwrap(); - parse_iso8601("20170304T1325", z).unwrap(); - // parse_iso8601("20170304T13Z", z).unwrap(); // ditto - // parse_iso8601("20170304T13", z).unwrap(); // ditto - parse_iso8601("20170304", z).unwrap(); - // parse_iso8601("201703", z).unwrap(); // ditto - parse_iso8601("2017031", z).unwrap(); - // parse_iso8601("2017", z).unwrap(); // ditto -} diff --git a/sop/src/errors.rs b/sop/src/errors.rs deleted file mode 100644 index 0eae86b7..00000000 --- a/sop/src/errors.rs +++ /dev/null @@ -1,101 +0,0 @@ -/// Errors defined by the Stateless OpenPGP Protocol. -#[derive(thiserror::Error, Debug, Clone)] -pub enum Error { - /// No acceptable signatures found ("sop verify"). - #[error("No acceptable signatures found")] - NoSignature, - - /// Asymmetric algorithm unsupported ("sop encrypt"). - #[error("Asymmetric algorithm unsupported")] - UnsupportedAsymmetricAlgo, - - /// Certificate not encryption-capable (e.g., expired, revoked, - /// unacceptable usage flags) ("sop encrypt"). - #[error("Certificate not encryption-capable")] - CertCannotEncrypt, - - /// Missing required argument. - #[error("Missing required argument")] - MissingArg, - - /// Incomplete verification instructions ("sop decrypt"). - #[error("Incomplete verification instructions")] - IncompleteVerification, - - /// Unable to decrypt ("sop decrypt"). - #[error("Unable to decrypt")] - CannotDecrypt, - - /// Non-"UTF-8" or otherwise unreliable password ("sop encrypt"). - #[error("Non-UTF-8 or otherwise unreliable password")] - PasswordNotHumanReadable, - - /// Unsupported option. - #[error("Unsupported option")] - UnsupportedOption, - - /// Invalid data type (no secret key where "KEY" expected, etc). - #[error("Invalid data type")] - BadData, - - /// Non-text input where text expected. - #[error("Non-text input where text expected")] - ExpectedText, - - /// Output file already exists. - #[error("Output file already exists")] - OutputExists, - - /// Input file does not exist. - #[error("Input file does not exist")] - MissingInput, - - /// A "KEY" input is protected (locked) with a password, and "sop" cannot - /// unlock it. - #[error("A KEY input is protected with a password")] - KeyIsProtected, - - /// Unsupported subcommand. - #[error("Unsupported subcommand")] - UnsupportedSubcommand, - - /// An indirect parameter is a special designator (it starts with "@") but - /// "sop" does not know how to handle the prefix. - #[error("An indirect parameter is a special designator with unknown prefix")] - UnsupportedSpecialPrefix, - - /// A indirect input parameter is a special designator (it starts with - /// "@"), and a filename matching the designator is actually present. - #[error("A indirect input parameter is a special designator matches file")] - AmbiguousInput, -} - -impl From<Error> for i32 { - fn from(e: Error) -> Self { - use Error::*; - match e { - NoSignature => 3, - UnsupportedAsymmetricAlgo => 13, - CertCannotEncrypt => 17, - MissingArg => 19, - IncompleteVerification => 23, - CannotDecrypt => 29, - PasswordNotHumanReadable => 31, - UnsupportedOption => 37, - BadData => 41, - ExpectedText => 53, - OutputExists => 59, - MissingInput => 61, - KeyIsProtected => 67, - UnsupportedSubcommand => 69, - UnsupportedSpecialPrefix => 71, - AmbiguousInput => 73, - } - } -} - -/// Prints the error and causes, if any. -pub fn print_error_chain(err: &anyhow::Error) { - eprintln!(" {}", err); - err.chain().skip(1).for_each(|cause| eprintln!(" because: {}", cause)); -} diff --git a/sop/src/main.rs b/sop/src/main.rs deleted file mode 100644 index 96bf4628..00000000 --- a/sop/src/main.rs +++ /dev/null @@ -1,786 +0,0 @@ -//! An implementation of the Stateless OpenPGP Command Line Interface -//! using Sequoia. -//! -//! This implements a subset of the [Stateless OpenPGP Command Line -//! Interface] using the Sequoia OpenPGP implementation. -//! -//! [Stateless OpenPGP Command Line Interface]: https://datatracker.ietf.org/doc/draft-dkg-openpgp-stateless-cli/ - -use std::collections::HashMap; -use std::convert::TryFrom; -use std::io::{self, Read, Write}; - -use anyhow::Context; -use structopt::StructOpt; - -use sequoia_openpgp as openpgp; -use openpgp::{ - armor, - Cert, - Fingerprint, - KeyID, - KeyHandle, - Packet, -}; -use openpgp::crypto::{self, Password, SessionKey}; -use openpgp::fmt::hex; -use openpgp::types::*; -use openpgp::packet::{key, signature, Key, PKESK, SKESK}; -use openpgp::parse::{Parse, PacketParser, PacketParserResult, stream::*}; -use openpgp::policy::Policy; -use openpgp::cert::prelude::*; -use openpgp::serialize::{ - Serialize, - stream::*, - stream::padding::Padder, -}; -use openpgp::policy::StandardPolicy; - -mod errors; -use errors::{Error, print_error_chain}; -type Result<T> = anyhow::Result<T>; - -mod cli; -use cli::{ - SOP, SignAs, EncryptAs, ArmorKind, - load_file, create_file, load_certs, load_keys, frob_passwords, -}; -mod dates; - -fn main() { - use std::process::exit; - - match real_main() { - Ok(()) => (), - Err(e) => { - print_error_chain(&e); - if let Ok(e) = e.downcast::<Error>() { - exit(e.into()) - } - exit(1); - }, - } -} - -fn real_main() -> Result<()> { - let p = &StandardPolicy::default(); - - match SOP::from_args() { - SOP::Version {} => { - println!("Sequoia-SOP {}", openpgp::VERSION); - }, - - SOP::GenerateKey { no_armor, mut userids, } => { - userids.reverse(); - let mut builder = CertBuilder::general_purpose(None, userids.pop()); - for u in userids { - builder = builder.add_userid(u); - } - let (cert, _) = builder.generate()?; - - let mut sink = stdout(no_armor, armor::Kind::SecretKey)?; - cert.as_tsk().serialize(&mut sink)?; - sink.finalize()?; - }, - - SOP::ExtractCert { no_armor, } => { - let cert = Cert::from_reader(&mut io::stdin())?; - let mut sink = stdout(no_armor, armor::Kind::PublicKey)?; - cert.serialize(&mut sink)?; - sink.finalize()?; - }, - - SOP::Sign { no_armor, as_, keys, } => { - let mut data = Vec::new(); - io::stdin().read_to_end(&mut data)?; - if let SignAs::Text = as_ { - if let Err(e) = std::str::from_utf8(&data) { - return Err(anyhow::Error::from(Error::ExpectedText)) - .context(e.to_string()); - } - } - - let tsks = load_keys(keys)?; - if tsks.is_empty() { - return Err(anyhow::Error::from(Error::MissingArg)) - .context("Expected at least one certificate"); - } - let mut signers = Vec::new(); - let mut hash_algos = vec![ - HashAlgorithm::SHA512, - HashAlgorithm::SHA384, - HashAlgorithm::SHA256, - HashAlgorithm::SHA224, - HashAlgorithm::RipeMD, - HashAlgorithm::SHA1, - HashAlgorithm::MD5, - ]; - for tsk in tsks { - let tsk = tsk.with_policy(p, None).map_err(|e| { - anyhow::Error::from(Error::CertCannotEncrypt) // XXX - .context(format!("Key {} not valid: {}", tsk, e)) - })?; - if let Some(p) = tsk.preferred_hash_algorithms() { - hash_algos.retain(|a| p.contains(a)); - } - - let mut one = false; - for key in tsk.keys() - .supported() - .secret() - .alive() - .revoked(false) - .for_signing() - .map(|ka| ka.key()) - { - if key.secret().is_encrypted() { - return Err(Error::KeyIsProtected.into()); - } - signers.push(key.clone().into_keypair() - .expect("not encrypted")); - one = true; - // Exactly one signature per supplied key. - break; - } - - if ! one { - return Err(anyhow::Error::from(Error::CertCannotEncrypt)) - .context(format!("Cert {} not capable of signing", - tsk)); - } - } - - let message = stdout(no_armor, armor::Kind::Signature)?; - let mut signer = Signer::with_template( - message, signers.pop().expect("at least one"), - signature::SignatureBuilder::new(as_.into())) - .hash_algo(hash_algos.get(0).cloned().unwrap_or_default())? - .detached(); - for s in signers { - signer = signer.add_signer(s); - } - let mut message = signer.build()?; - message.write_all(&data)?; - message.finalize()?; - }, - - SOP::Verify { not_before, not_after, signatures, certs, } => { - let certs = load_certs(certs)?; - let signatures = load_file(signatures)?; - let helper = VHelper::new(io::stdout(), - 1, - not_before.map(|d| d.into()), - not_after.map(|d| d.into()), - certs); - let mut v = - DetachedVerifierBuilder::from_reader(signatures)? - .with_policy(p, None, helper)?; - v.verify_reader(io::stdin())?; - }, - - SOP::Encrypt { no_armor, as_, with_password, sign_with, certs, } => - { - let mut data = Vec::new(); - io::stdin().read_to_end(&mut data)?; - if let EncryptAs::Text = as_ { - if let Err(e) = std::str::from_utf8(&data) { - return Err(anyhow::Error::from(Error::ExpectedText)) - .context(e.to_string()); - } - } - - let passwords = frob_passwords(with_password)?; - - let tsks = load_keys(sign_with)?; - let mut signers = Vec::new(); - for tsk in tsks { - let mut one = false; - for key in tsk.keys().with_policy(p, None) - .supported() - .secret() - .alive() - .revoked(false) - .for_signing() - .map(|ka| ka.key()) - { - if key.secret().is_encrypted() { - return Err(Error::KeyIsProtected.into()); - } - signers.push(key.clone().into_keypair() - .expect("not encrypted")); - one = true; - // Exactly one signature per supplied key. - break; - } - - if ! one { - return Err(anyhow::Error::from(Error::CertCannotEncrypt)) // XXX - .context(format!("Cert {} not capable of signing", - tsk)); - } - } - - let certs = load_certs(certs)?; - - if certs.is_empty() && passwords.is_empty() { - return Err(anyhow::Error::from(Error::MissingArg)) - .context("Neither passwords or certs given"); - } - - // Compute recipients and algorithms. - let mut recipients: Vec<Recipient> = Vec::new(); - // Somewhat arbitrary order of preference. - let mut hash_algos = vec![ - HashAlgorithm::SHA512, - HashAlgorithm::SHA384, - HashAlgorithm::SHA256, - HashAlgorithm::SHA224, - HashAlgorithm::RipeMD, - HashAlgorithm::SHA1, - HashAlgorithm::MD5, - ]; - let mut symmetric_algos = vec![ - SymmetricAlgorithm::AES256, - SymmetricAlgorithm::AES192, - SymmetricAlgorithm::AES128, - SymmetricAlgorithm::Camellia256, - SymmetricAlgorithm::Camellia192, - SymmetricAlgorithm::Camellia128, - SymmetricAlgorithm::Blowfish, - SymmetricAlgorithm::Twofish, - SymmetricAlgorithm::CAST5, - SymmetricAlgorithm::IDEA, - SymmetricAlgorithm::TripleDES, - ]; - let mut aead_algos = vec![ - AEADAlgorithm::EAX, - AEADAlgorithm::OCB, - ]; - for cert in certs.iter() { - let cert = cert.with_policy(p, None).map_err(|e| { - anyhow::Error::from(Error::CertCannotEncrypt) - .context(format!("Cert {} not valid: {}", cert, e)) - })?; - - // If the recipients has preferences, compute the - // intersection with our list. - if let Some(p) = cert.preferred_hash_algorithms() { - hash_algos.retain(|a| p.contains(a)); - } - if let Some(p) = cert.preferred_symmetric_algorithms() { - symmetric_algos.retain(|a| p.contains(a)); - } - if let Some(p) = cert.preferred_aead_algorithms() { - aead_algos.retain(|a| p.contains(a)); - } - - // If the cert doesn't advertise support for AEAD, - // disable it. - if ! cert.features().map(|f| f.supports_aead()).unwrap_or(false) - { - aead_algos.clear(); - } - - let mut one = false; - for key in cert.keys() - .supported() - .alive() - .revoked(false) - .for_storage_encryption() - .for_transport_encryption() - .map(|ka| ka.key()) - { - recipients.push(key.into()); - |