summaryrefslogtreecommitdiffstats
path: root/tool
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-12-20 17:42:00 +0100
committerJustus Winter <justus@sequoia-pgp.org>2019-01-15 14:09:15 +0100
commit5bef3bde45f71126cdca3e8ad30b1047287c843a (patch)
treee3b45081b6fc33115ce199716824d418d088f26c /tool
parentf8a502c6b18e097bf1082877f3b6b2f5c99f3a41 (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.rs42
-rw-r--r--tool/src/commands/sign.rs28
-rw-r--r--tool/tests/sq-sign.rs10
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();