summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2021-01-28 11:20:19 +0100
committerJustus Winter <justus@sequoia-pgp.org>2021-01-28 11:20:19 +0100
commit080738631d80e4df4181cd848a02120e7a5e0af8 (patch)
tree252c205e76683053abdb3f8dea2731768f395623
parentd97a6a10067bfc7e7fc95bc280d7cec400dc0615 (diff)
sq: Make Autocrypt support optional.
-rw-r--r--sq/Cargo.toml4
-rw-r--r--sq/build.rs1
-rw-r--r--sq/src/commands/autocrypt.rs60
-rw-r--r--sq/src/commands/mod.rs2
-rw-r--r--sq/src/sq.rs45
-rw-r--r--sq/src/sq_cli.rs182
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
}