summaryrefslogtreecommitdiffstats
path: root/tool
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-05-31 17:46:58 +0200
committerJustus Winter <justus@sequoia-pgp.org>2018-05-31 18:41:20 +0200
commit211b1f91fa18e19af2091615f200ec5735dcf658 (patch)
treea378df27555c1be89d6b25e6274dd6c2a2226b38 /tool
parent62f6d9bf5575dfc4621246acd532f3302ef9118e (diff)
tool: Implement encryption subcommand.
Diffstat (limited to 'tool')
-rw-r--r--tool/src/cli.rs29
-rw-r--r--tool/src/commands.rs46
-rw-r--r--tool/src/sq-usage.rs21
-rw-r--r--tool/src/sq.rs19
4 files changed, 114 insertions, 1 deletions
diff --git a/tool/src/cli.rs b/tool/src/cli.rs
index efb2136a..f965e4e7 100644
--- a/tool/src/cli.rs
+++ b/tool/src/cli.rs
@@ -34,6 +34,35 @@ pub fn build() -> App<'static, 'static> {
.long("hex")
.short("x")
.help("Print a hexdump (implies --dump)")))
+ .subcommand(SubCommand::with_name("encrypt")
+ .about("Encrypts a message")
+ .arg(Arg::with_name("input").value_name("FILE")
+ .long("input")
+ .short("i")
+ .help("Sets the input file to use"))
+ .arg(Arg::with_name("output").value_name("FILE")
+ .long("output")
+ .short("o")
+ .help("Sets the output file to use"))
+ .arg(Arg::with_name("armor")
+ .long("armor")
+ .short("A")
+ .help("Write armored data to file"))
+ .arg(Arg::with_name("recipient")
+ .long("recipient")
+ .short("r")
+ .multiple(true)
+ .takes_value(true)
+ .value_name("LABEL")
+ .number_of_values(1)
+ .help("Recipient to encrypt for \
+ (can be given multiple times)"))
+ .arg(Arg::with_name("symmetric")
+ .long("symmetric")
+ .short("s")
+ .multiple(true)
+ .help("Encrypt with a password \
+ (can be given multiple times)")))
.subcommand(SubCommand::with_name("enarmor")
.about("Applies ASCII Armor to a file")
.arg(Arg::with_name("input").value_name("FILE")
diff --git a/tool/src/commands.rs b/tool/src/commands.rs
index f7a42318..2ad9ebda 100644
--- a/tool/src/commands.rs
+++ b/tool/src/commands.rs
@@ -1,9 +1,13 @@
-use failure;
+use failure::{self, ResultExt};
use std::io;
use rpassword;
extern crate openpgp;
use openpgp::{Packet, Tag};
+use openpgp::serialize::stream::{
+ wrap, LiteralWriter, Encryptor, EncryptionMode,
+};
+extern crate sequoia_store as store;
// Indent packets according to their recursion level.
const INDENT: &'static str
@@ -116,6 +120,46 @@ pub fn decrypt(input: &mut io::Read, output: &mut io::Write,
Ok(())
}
+pub fn encrypt(store: &mut store::Store,
+ input: &mut io::Read, output: &mut io::Write,
+ npasswords: usize, recipients: Vec<&str>)
+ -> Result<(), failure::Error> {
+ let mut tpks = Vec::with_capacity(recipients.len());
+ for r in recipients {
+ tpks.push(store.lookup(r).context("No such key found")?.tpk()?);
+ }
+ let mut passwords = Vec::with_capacity(npasswords);
+ for n in 0..npasswords {
+ let nprompt = format!("Enter passphrase {}: ", n + 1);
+ passwords.push(rpassword::prompt_password_stderr(
+ if npasswords > 1 {
+ &nprompt
+ } else {
+ "Enter passphrase: "
+ })?);
+ }
+
+ // Build a vector of references to hand to Encryptor.
+ let recipients: Vec<&openpgp::TPK> = tpks.iter().collect();
+ let passwords_: Vec<&[u8]> =
+ passwords.iter().map(|p| p.as_bytes()).collect();
+
+ // We want to encrypt a literal data packet.
+ let encryptor = Encryptor::new(wrap(output),
+ &passwords_,
+ &recipients,
+ EncryptionMode::AtRest)
+ .context("Failed to create encryptor")?;
+ let mut literal_writer = LiteralWriter::new(encryptor, 'b', None, 0)
+ .context("Failed to create literal writer")?;
+
+ // Finally, copy stdin to our writer stack to encrypt the data.
+ io::copy(input, &mut literal_writer)
+ .context("Failed to encrypt")?;
+
+ Ok(())
+}
+
pub fn dump(input: &mut io::Read, output: &mut io::Write, map: bool)
-> Result<(), failure::Error> {
let mut ppo
diff --git a/tool/src/sq-usage.rs b/tool/src/sq-usage.rs
index 76add0e9..a48b6ddb 100644
--- a/tool/src/sq-usage.rs
+++ b/tool/src/sq-usage.rs
@@ -22,6 +22,7 @@
//! decrypt Decrypts an OpenPGP message
//! dump Lists OpenPGP packets
//! enarmor Applies ASCII Armor to a file
+//! encrypt Encrypts a message
//! help Prints this message or the help of the given subcommand(s)
//! keyserver Interacts with keyservers
//! list Lists key stores and known keys
@@ -99,6 +100,26 @@
//! -o, --output <FILE> Sets the output file to use
//! ```
//!
+//! ## Subcommand encrypt
+//!
+//! ```text
+//! Encrypts a message
+//!
+//! USAGE:
+//! sq encrypt [FLAGS] [OPTIONS]
+//!
+//! FLAGS:
+//! -A, --armor Write armored data to file
+//! -h, --help Prints help information
+//! -s, --symmetric Encrypt with a password (can be given multiple times)
+//! -V, --version Prints version information
+//!
+//! OPTIONS:
+//! -i, --input <FILE> Sets the input file to use
+//! -o, --output <FILE> Sets the output file to use
+//! -r, --recipient <LABEL>... Recipient to encrypt for (can be given multiple times)
+//! ```
+//!
//! ## Subcommand keyserver
//!
//! ```text
diff --git a/tool/src/sq.rs b/tool/src/sq.rs
index dd7b9b0e..ea484e70 100644
--- a/tool/src/sq.rs
+++ b/tool/src/sq.rs
@@ -68,6 +68,25 @@ fn real_main() -> Result<(), failure::Error> {
commands::decrypt(&mut input, &mut output,
m.is_present("dump"), m.is_present("hex"))?;
},
+ ("encrypt", Some(m)) => {
+ let mut input = open_or_stdin(m.value_of("input"))?;
+ let mut output = create_or_stdout(m.value_of("output"))?;
+ let mut output = if m.is_present("armor") {
+ Box::new(armor::Writer::new(&mut output,
+ armor::Kind::Message))
+ } else {
+ output
+ };
+ let mut store = Store::open(&ctx, store_name)
+ .context("Failed to open the store")?;
+ let recipients = m.values_of("recipient")
+ .map(|r| r.collect())
+ .unwrap_or(vec![]);
+ commands::encrypt(&mut store, &mut input, &mut output,
+ m.occurrences_of("symmetric") as usize,
+ recipients)?;
+ },
+
("enarmor", Some(m)) => {
let mut input = open_or_stdin(m.value_of("input"))?;
let mut output = create_or_stdout(m.value_of("output"))?;