diff options
Diffstat (limited to 'sq')
-rw-r--r-- | sq/Cargo.toml | 4 | ||||
-rw-r--r-- | sq/build.rs | 1 | ||||
-rw-r--r-- | sq/src/commands/autocrypt.rs | 60 | ||||
-rw-r--r-- | sq/src/commands/mod.rs | 2 | ||||
-rw-r--r-- | sq/src/sq.rs | 45 | ||||
-rw-r--r-- | sq/src/sq_cli.rs | 182 |
6 files changed, 166 insertions, 128 deletions
diff --git a/sq/Cargo.toml b/sq/Cargo.toml index bc49cb0d..66f06274 100644 --- a/sq/Cargo.toml +++ b/sq/Cargo.toml @@ -28,7 +28,7 @@ maintenance = { status = "actively-developed" } [dependencies] buffered-reader = { path = "../buffered-reader", version = "1.0.0", default-features = false } sequoia-openpgp = { path = "../openpgp", version = "1.0.0", default-features = false } -sequoia-autocrypt = { path = "../autocrypt", version = "0.23", default-features = false } +sequoia-autocrypt = { path = "../autocrypt", version = "0.23", default-features = false, optional = true } sequoia-net = { path = "../net", version = "0.23", default-features = false, optional = true } anyhow = "1.0.18" chrono = "0.4.10" @@ -55,6 +55,7 @@ default = [ "buffered-reader/compression", "sequoia-openpgp/default", "net", + "autocrypt", ] crypto-nettle = ["sequoia-openpgp/crypto-nettle"] crypto-cng = ["sequoia-openpgp/crypto-cng"] @@ -62,3 +63,4 @@ compression = ["buffered-reader/compression", "sequoia-openpgp/compression"] compression-deflate = ["buffered-reader/compression-deflate", "sequoia-openpgp/compression-deflate"] compression-bzip2 = ["buffered-reader/compression-bzip2", "sequoia-openpgp/compression-bzip2"] net = ["sequoia-net", "tokio"] +autocrypt = ["sequoia-autocrypt"] diff --git a/sq/build.rs b/sq/build.rs index 14818eee..4671370b 100644 --- a/sq/build.rs +++ b/sq/build.rs @@ -16,6 +16,7 @@ fn main() { let mut sq = sq_cli::configure( clap::App::new("sq").set_term_width(80), cfg!(feature = "net"), + cfg!(feature = "autocrypt"), ); let mut main = fs::File::create("src/sq-usage.rs").unwrap(); dump_help(&mut main, diff --git a/sq/src/commands/autocrypt.rs b/sq/src/commands/autocrypt.rs new file mode 100644 index 00000000..b607c128 --- /dev/null +++ b/sq/src/commands/autocrypt.rs @@ -0,0 +1,60 @@ +use sequoia_openpgp as openpgp; +use openpgp::{ + Cert, + Result, + armor, + parse::Parse, + serialize::Serialize, +}; +use sequoia_autocrypt as autocrypt; + +use crate::{ + Config, + create_or_stdout, + create_or_stdout_pgp, + open_or_stdin, +}; + +pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> { + match m.subcommand() { + ("decode", Some(m)) => { + let input = open_or_stdin(m.value_of("input"))?; + let mut output = + create_or_stdout_pgp(m.value_of("output"), + config.force, + m.is_present("binary"), + armor::Kind::PublicKey)?; + let ac = autocrypt::AutocryptHeaders::from_reader(input)?; + for h in &ac.headers { + if let Some(ref cert) = h.key { + cert.serialize(&mut output)?; + } + } + output.finalize()?; + }, + ("encode-sender", Some(m)) => { + let input = open_or_stdin(m.value_of("input"))?; + let mut output = create_or_stdout(m.value_of("output"), + config.force)?; + let cert = Cert::from_reader(input)?; + let addr = m.value_of("address").map(|a| a.to_string()) + .or_else(|| { + cert.with_policy(&config.policy, None) + .and_then(|vcert| vcert.primary_userid()).ok() + .map(|ca| ca.userid().to_string()) + }); + let ac = autocrypt::AutocryptHeader::new_sender( + &config.policy, + &cert, + &addr.ok_or(anyhow::anyhow!( + "No well-formed primary userid found, use \ + --address to specify one"))?, + m.value_of("prefer-encrypt").expect("has default"))?; + write!(&mut output, "Autocrypt: ")?; + ac.serialize(&mut output)?; + }, + _ => unreachable!(), + } + + Ok(()) +} diff --git a/sq/src/commands/mod.rs b/sq/src/commands/mod.rs index ca0e03e7..6eb0fb74 100644 --- a/sq/src/commands/mod.rs +++ b/sq/src/commands/mod.rs @@ -35,6 +35,8 @@ use crate::{ create_or_stdout_pgp, }; +#[cfg(feature = "autocrypt")] +pub mod autocrypt; pub mod decrypt; pub use self::decrypt::decrypt; mod sign; diff --git a/sq/src/sq.rs b/sq/src/sq.rs index 9e11e44c..f04f4b36 100644 --- a/sq/src/sq.rs +++ b/sq/src/sq.rs @@ -2,7 +2,7 @@ use anyhow::Context as _; use std::fs::OpenOptions; -use std::io::{self, Write}; +use std::io; use std::path::{Path, PathBuf}; use std::time::Duration; use chrono::{DateTime, offset::Utc}; @@ -15,7 +15,6 @@ use openpgp::{ Result, }; use crate::openpgp::{armor, Cert}; -use sequoia_autocrypt as autocrypt; use crate::openpgp::crypto::Password; use crate::openpgp::fmt::hex; use crate::openpgp::types::KeyFlags; @@ -541,46 +540,8 @@ fn main() -> Result<()> { let mut filter = armor::Reader::new(&mut input, None); io::copy(&mut filter, &mut output)?; }, - ("autocrypt", Some(m)) => { - match m.subcommand() { - ("decode", Some(m)) => { - let input = open_or_stdin(m.value_of("input"))?; - let mut output = - create_or_stdout_pgp(m.value_of("output"), force, - m.is_present("binary"), - armor::Kind::PublicKey)?; - let ac = autocrypt::AutocryptHeaders::from_reader(input)?; - for h in &ac.headers { - if let Some(ref cert) = h.key { - cert.serialize(&mut output)?; - } - } - output.finalize()?; - }, - ("encode-sender", Some(m)) => { - let input = open_or_stdin(m.value_of("input"))?; - let mut output = create_or_stdout(m.value_of("output"), - force)?; - let cert = Cert::from_reader(input)?; - let addr = m.value_of("address").map(|a| a.to_string()) - .or_else(|| { - cert.with_policy(policy, None) - .and_then(|vcert| vcert.primary_userid()).ok() - .map(|ca| ca.userid().to_string()) - }); - let ac = autocrypt::AutocryptHeader::new_sender( - policy, - &cert, - &addr.ok_or(anyhow::anyhow!( - "No well-formed primary userid found, use \ - --address to specify one"))?, - m.value_of("prefer-encrypt").expect("has default"))?; - write!(&mut output, "Autocrypt: ")?; - ac.serialize(&mut output)?; - }, - _ => unreachable!(), - } - }, + #[cfg(feature = "autocrypt")] + ("autocrypt", Some(m)) => commands::autocrypt::dispatch(config, m)?, ("inspect", Some(m)) => { let mut output = create_or_stdout(m.value_of("output"), force)?; diff --git a/sq/src/sq_cli.rs b/sq/src/sq_cli.rs index 7833a3bf..d7efdd96 100644 --- a/sq/src/sq_cli.rs +++ b/sq/src/sq_cli.rs @@ -5,6 +5,7 @@ use clap::{App, Arg, ArgGroup, SubCommand, AppSettings}; pub fn build() -> App<'static, 'static> { configure(App::new("sq"), cfg!(feature = "net"), + cfg!(feature = "autocrypt"), ) } @@ -21,6 +22,7 @@ pub fn build() -> App<'static, 'static> { pub fn configure( app: App<'static, 'static>, feature_net: bool, + feature_autocrypt: bool, ) -> App<'static, 'static> { let version = Box::leak( format!("{} (sequoia-openpgp {})", @@ -395,91 +397,6 @@ $ sq dearmor ascii-message.pgp .help("Writes to FILE or stdout if omitted")) ) - .subcommand(SubCommand::with_name("autocrypt") - .display_order(400) - .about("Communicates certificates using Autocrypt") - .long_about( -"Communicates certificates using Autocrypt - -Autocrypt is a standard for mail user agents to provide convenient -end-to-end encryption of emails. This subcommand provides a limited -way to produce and consume headers that are used by Autocrypt to -communicate certificates between clients. - -See https://autocrypt.org/ -") - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(SubCommand::with_name("decode") - .about("Reads Autocrypt-encoded certificates") - .long_about( -"Reads Autocrypt-encoded certificates - -Given an autocrypt header (or an key-gossip header), this command -extracts the certificate encoded within it. - -The converse operation is 'sq autocrypt encode-sender'. -") - .after_help( -"EXAMPLES: - -# Extract all certificates from a mail -$ sq autocrypt decode autocrypt.eml -") - .arg(Arg::with_name("input") - .value_name("FILE") - .help("Reads from FILE or stdin if omitted")) - .arg(Arg::with_name("output") - .short("o").long("output").value_name("FILE") - .help("Writes to FILE or stdout if omitted")) - .arg(Arg::with_name("binary") - .short("B").long("binary") - .help("Emits binary data")) - ) - .subcommand(SubCommand::with_name("encode-sender") - .about("Encodes a certificate into \ - an Autocrypt header") - .long_about( -"Encodes a certificate into an Autocrypt header - -A certificate can be encoded and included in a header of an email -message. This command encodes the certificate, adds the senders email -address (which must match the one used in the 'From' header), and the -senders 'prefer-encrypt' state (see the Autocrypt spec for more -information). - -The converse operation is 'sq autocrypt decode'. -") - .after_help( -"EXAMPLES: - -# Encodes a certificate -$ sq autocrypt encode-sender juliet.pgp - -# Encodes a certificate with an explicit sender address -$ sq autocrypt encode-sender --email juliet@example.org juliet.pgp - -# Encodes a certificate while indicating the willingness to encrypt -$ sq autocrypt encode-sender --prefer-encrypt mutual juliet.pgp -") - .arg(Arg::with_name("input") - .value_name("FILE") - .help("Reads from FILE or stdin if omitted")) - .arg(Arg::with_name("output") - .short("o").long("output").value_name("FILE") - .help("Writes to FILE or stdout if omitted")) - .arg(Arg::with_name("address") - .long("email").value_name("ADDRESS") - .help("Sets the address \ - [default: primary userid]")) - .arg(Arg::with_name("prefer-encrypt") - .long("prefer-encrypt") - .possible_values(&["nopreference", - "mutual"]) - .default_value("nopreference") - .help("Sets the prefer-encrypt \ - attribute")) - ) - ) .subcommand(SubCommand::with_name("inspect") .display_order(600) @@ -1335,5 +1252,100 @@ $ sq packet join juliet.pgp-[0-3]* ) }; + let app = if ! feature_autocrypt { + // Without Autocrypt support. + app + } else { + // With Autocrypt support. + app.subcommand( + SubCommand::with_name("autocrypt") + .display_order(400) + .about("Communicates certificates using Autocrypt") + .long_about( +"Communicates certificates using Autocrypt + +Autocrypt is a standard for mail user agents to provide convenient +end-to-end encryption of emails. This subcommand provides a limited +way to produce and consume headers that are used by Autocrypt to +communicate certificates between clients. + +See https://autocrypt.org/ +") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("decode") + .about("Reads Autocrypt-encoded certificates") + .long_about( +"Reads Autocrypt-encoded certificates + +Given an autocrypt header (or an key-gossip header), this command +extracts the certificate encoded within it. + +The converse operation is 'sq autocrypt encode-sender'. +") + .after_help( +"EXAMPLES: + +# Extract all certificates from a mail +$ sq autocrypt decode autocrypt.eml +") + .arg(Arg::with_name("input") + .value_name("FILE") + .help("Reads from FILE or stdin if omitted")) + .arg(Arg::with_name("output") + .short("o").long("output").value_name("FILE") + .help("Writes to FILE or stdout if omitted")) + .arg(Arg::with_name("binary") + .short("B").long("binary") + .help("Emits binary data")) + ) + .subcommand( + SubCommand::with_name("encode-sender") + .about("Encodes a certificate into \ + an Autocrypt header") + .long_about( +"Encodes a certificate into an Autocrypt header + +A certificate can be encoded and included in a header of an email +message. This command encodes the certificate, adds the senders email +address (which must match the one used in the 'From' header), and the +senders 'prefer-encrypt' state (see the Autocrypt spec for more +information). + +The converse operation is 'sq autocrypt decode'. +") + .after_help( +"EXAMPLES: + +# Encodes a certificate +$ sq autocrypt encode-sender juliet.pgp + +# Encodes a certificate with an explicit sender address +$ sq autocrypt encode-sender --email juliet@example.org juliet.pgp + +# Encodes a certificate while indicating the willingness to encrypt +$ sq autocrypt encode-sender --prefer-encrypt mutual juliet.pgp +") + .arg(Arg::with_name("input") + .value_name("FILE") + .help("Reads from FILE or stdin if omitted")) + .arg(Arg::with_name("output") + .short("o").long("output").value_name("FILE") + .help("Writes to FILE or stdout if omitted")) + .arg(Arg::with_name("address") + .long("email").value_name("ADDRESS") + .help("Sets the address \ + [default: primary userid]")) + .arg(Arg::with_name("prefer-encrypt") + .long("prefer-encrypt") + .possible_values(&["nopreference", + "mutual"]) + .default_value("nopreference") + .help("Sets the prefer-encrypt \ + attribute")) + ) + ) + }; + app } |