diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2018-05-31 17:46:58 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2018-05-31 18:41:20 +0200 |
commit | 211b1f91fa18e19af2091615f200ec5735dcf658 (patch) | |
tree | a378df27555c1be89d6b25e6274dd6c2a2226b38 /tool | |
parent | 62f6d9bf5575dfc4621246acd532f3302ef9118e (diff) |
tool: Implement encryption subcommand.
Diffstat (limited to 'tool')
-rw-r--r-- | tool/src/cli.rs | 29 | ||||
-rw-r--r-- | tool/src/commands.rs | 46 | ||||
-rw-r--r-- | tool/src/sq-usage.rs | 21 | ||||
-rw-r--r-- | tool/src/sq.rs | 19 |
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"))?; |