summaryrefslogtreecommitdiffstats
path: root/sop/src
diff options
context:
space:
mode:
Diffstat (limited to 'sop/src')
-rw-r--r--sop/src/cli.rs334
-rw-r--r--sop/src/dates.rs95
-rw-r--r--sop/src/errors.rs101
-rw-r--r--sop/src/main.rs786
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());
-