diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2018-12-20 17:42:00 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2019-01-15 14:09:15 +0100 |
commit | 5bef3bde45f71126cdca3e8ad30b1047287c843a (patch) | |
tree | e3b45081b6fc33115ce199716824d418d088f26c /tool | |
parent | f8a502c6b18e097bf1082877f3b6b2f5c99f3a41 (diff) |
openpgp: Hand a Vec<crypto::Signer> to stream::Signer.
- Using `crypto::Signer`s has several benefits. First, it shifts
the decision which key to use to the caller, moving policy out of
the caller. Second, it forces the caller to deal with encrypted
keys. Finally, it allows us to use remote keys like smart cards
in the future.
- Fixes #142.
Diffstat (limited to 'tool')
-rw-r--r-- | tool/src/commands/mod.rs | 42 | ||||
-rw-r--r-- | tool/src/commands/sign.rs | 28 | ||||
-rw-r--r-- | tool/tests/sq-sign.rs | 10 |
3 files changed, 69 insertions, 11 deletions
diff --git a/tool/src/commands/mod.rs b/tool/src/commands/mod.rs index dc7b624d..c84bc788 100644 --- a/tool/src/commands/mod.rs +++ b/tool/src/commands/mod.rs @@ -9,7 +9,9 @@ use rpassword; extern crate sequoia_openpgp as openpgp; use sequoia_core::Context; use openpgp::constants::DataFormat; +use openpgp::crypto; use openpgp::{TPK, KeyID, Result}; +use openpgp::packet::key::SecretKey; use openpgp::parse::{ Parse, PacketParserResult, @@ -35,6 +37,37 @@ fn tm2str(t: &time::Tm) -> String { time::strftime(TIMEFMT, t).expect("TIMEFMT is correct") } +/// Returns suitable signing keys from a given list of TPKs. +fn get_signing_keys(tpks: &[openpgp::TPK]) -> Result<Vec<crypto::KeyPair>> { + let mut keys = Vec::new(); + 'next_tpk: for tsk in tpks { + for key in tsk.select_signing_keys(None) { + if let Some(mut secret) = key.secret() { + let secret_mpis = match secret { + SecretKey::Encrypted { .. } => { + let password = rpassword::prompt_password_stderr( + &format!("Please enter password to decrypt {}/{}: ", + tsk, key)).unwrap(); + secret.decrypt(key.pk_algo(), &password.into()) + .expect("decryption failed") + }, + SecretKey::Unencrypted { ref mpis } => + mpis.clone(), + }; + + keys.push(crypto::KeyPair::new(key.clone(), secret_mpis) + .unwrap()); + break 'next_tpk; + } + } + + return Err(failure::err_msg( + format!("Found no suitable signing key on {}", tsk))); + } + + Ok(keys) +} + pub fn encrypt(store: &mut store::Store, input: &mut io::Read, output: &mut io::Write, npasswords: usize, recipients: Vec<&str>, @@ -54,6 +87,8 @@ pub fn encrypt(store: &mut store::Store, })?.into()); } + let mut signers = get_signing_keys(&signers)?; + // Build a vector of references to hand to Encryptor. let recipients: Vec<&openpgp::TPK> = tpks.iter().collect(); let passwords_: Vec<&openpgp::crypto::Password> = @@ -71,8 +106,11 @@ pub fn encrypt(store: &mut store::Store, // Optionally sign message. if ! signers.is_empty() { - let signers_: Vec<&openpgp::TPK> = signers.iter().collect(); - sink = Signer::with_intended_recipients(sink, &signers_, &recipients)?; + sink = Signer::with_intended_recipients( + sink, + signers.iter_mut().map(|s| -> &mut dyn crypto::Signer { s }) + .collect(), + &recipients)?; } let mut literal_writer = LiteralWriter::new(sink, DataFormat::Binary, diff --git a/tool/src/commands/sign.rs b/tool/src/commands/sign.rs index 00d2de33..119e66e3 100644 --- a/tool/src/commands/sign.rs +++ b/tool/src/commands/sign.rs @@ -7,8 +7,9 @@ use tempfile::NamedTempFile; extern crate sequoia_openpgp as openpgp; use openpgp::armor; use openpgp::constants::DataFormat; +use openpgp::crypto; use openpgp::{Packet, Error, Result}; -use openpgp::packet::Signature; +use openpgp::packet::signature::Signature; use openpgp::parse::{ Parse, PacketParserResult, @@ -81,6 +82,11 @@ fn sign_data(input: &mut io::Read, output_path: Option<&str>, output }; + let mut keypairs = super::get_signing_keys(&secrets)?; + let signers = keypairs.iter_mut() + .map(|s| -> &mut dyn crypto::Signer { s }) + .collect(); + // When extending a detached signature, prepend any existing // signatures first. for sig in prepend_sigs { @@ -90,12 +96,10 @@ fn sign_data(input: &mut io::Read, output_path: Option<&str>, // Stream an OpenPGP message. let sink = Message::new(output); - // Build a vector of references to hand to Signer. - let keys: Vec<&openpgp::TPK> = secrets.iter().collect(); let signer = if detached { - Signer::detached(sink, &keys) + Signer::detached(sink, signers) } else { - Signer::new(sink, &keys) + Signer::new(sink, signers) }.context("Failed to create signer")?; let mut writer = if detached { @@ -136,9 +140,16 @@ fn sign_message(input: &mut io::Read, output_path: Option<&str>, output }; + let mut keypairs = super::get_signing_keys(&secrets)?; + // We need to create the signers here, so that we can take() them + // once in the parsing loop. We cannot create the references in + // the loop, because the borrow checker does not understand that + // it happens only once. + let mut signers = Some(keypairs.iter_mut() + .map(|s| -> &mut dyn crypto::Signer { s }) + .collect::<Vec<&mut dyn crypto::Signer>>()); + let mut sink = Message::new(output); - // Build a vector of references to hand to Signer. - let keys: Vec<&openpgp::TPK> = secrets.iter().collect(); // Create a parser for the message to be notarized. let mut ppr @@ -203,7 +214,8 @@ fn sign_message(input: &mut io::Read, output_path: Option<&str>, State::AfterFirstSigGroup => { // After the first signature group, we push the signer // onto the writer stack. - sink = Signer::new(sink, &keys) + let signers = signers.take().expect("only happens once"); + sink = Signer::new(sink, signers) .context("Failed to create signer")?; state = State::Signing { signature_count: 0, }; }, diff --git a/tool/tests/sq-sign.rs b/tool/tests/sq-sign.rs index 28fcce64..90da8d8a 100644 --- a/tool/tests/sq-sign.rs +++ b/tool/tests/sq-sign.rs @@ -8,6 +8,8 @@ use tempfile::TempDir; extern crate sequoia_openpgp as openpgp; use openpgp::{Packet, PacketPile, TPK}; +use openpgp::crypto::KeyPair; +use openpgp::packet::key::SecretKey; use openpgp::constants::{CompressionAlgorithm, DataFormat, SignatureType}; use openpgp::parse::Parse; use openpgp::serialize::stream::{Message, Signer, Compressor, LiteralWriter}; @@ -206,8 +208,14 @@ fn sq_sign_append_on_compress_then_sign() { // message by foot. let tsk = TPK::from_file(&p("keys/dennis-simon-anton-private.pgp")) .unwrap(); + let key = tsk.select_signing_keys(None)[0]; + let sec = match key.secret() { + Some(SecretKey::Unencrypted { ref mpis }) => mpis, + _ => unreachable!(), + }; + let mut keypair = KeyPair::new(key.clone(), sec.clone()).unwrap(); let signer = Signer::new(Message::new(File::create(&sig0).unwrap()), - &[&tsk]) + vec![&mut keypair]) .unwrap(); let compressor = Compressor::new(signer, CompressionAlgorithm::Uncompressed) .unwrap(); |