summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--ffi/src/openpgp/mod.rs24
-rw-r--r--openpgp/Cargo.toml3
-rw-r--r--openpgp/examples/notarize.rs36
-rw-r--r--openpgp/examples/sign-detached.rs37
-rw-r--r--openpgp/examples/sign.rs36
-rw-r--r--openpgp/src/parse/stream.rs11
-rw-r--r--openpgp/src/serialize/stream.rs158
-rw-r--r--tool/src/commands/mod.rs42
-rw-r--r--tool/src/commands/sign.rs28
-rw-r--r--tool/tests/sq-sign.rs10
10 files changed, 262 insertions, 123 deletions
diff --git a/ffi/src/openpgp/mod.rs b/ffi/src/openpgp/mod.rs
index 16321497..f3316944 100644
--- a/ffi/src/openpgp/mod.rs
+++ b/ffi/src/openpgp/mod.rs
@@ -1173,7 +1173,8 @@ pub extern "system" fn sq_arbitrary_writer_new
pub extern "system" fn sq_signer_new
(ctx: *mut Context,
inner: *mut writer::Stack<'static, Cookie>,
- signers: *const &'static TPK, signers_len: size_t)
+ signers: *const *mut Box<self::openpgp::crypto::Signer>,
+ signers_len: size_t)
-> *mut writer::Stack<'static, Cookie>
{
let ctx = ffi_param_ref_mut!(ctx);
@@ -1182,7 +1183,13 @@ pub extern "system" fn sq_signer_new
let signers = unsafe {
slice::from_raw_parts(signers, signers_len)
};
- fry_box!(ctx, Signer::new(*inner, &signers))
+ let signers = signers.into_iter().map(
+ |s| -> &mut dyn self::openpgp::crypto::Signer {
+ let signer = *s;
+ ffi_param_ref_mut!(signer).as_mut()
+ }
+ ).collect();
+ fry_box!(ctx, Signer::new(*inner, signers))
}
/// Creates a signer for a detached signature.
@@ -1190,16 +1197,23 @@ pub extern "system" fn sq_signer_new
pub extern "system" fn sq_signer_new_detached
(ctx: *mut Context,
inner: *mut writer::Stack<'static, Cookie>,
- signers: Option<&&'static TPK>, signers_len: size_t)
+ signers: *const *mut Box<self::openpgp::crypto::Signer>,
+ signers_len: size_t)
-> *mut writer::Stack<'static, Cookie>
{
let ctx = ffi_param_ref_mut!(ctx);
let inner = ffi_param_move!(inner);
- let signers = signers.expect("Signers is NULL");
+ let signers = ffi_param_ref!(signers);
let signers = unsafe {
slice::from_raw_parts(signers, signers_len)
};
- fry_box!(ctx, Signer::detached(*inner, &signers))
+ let signers = signers.into_iter().map(
+ |s| -> &mut dyn self::openpgp::crypto::Signer {
+ let signer = *s;
+ ffi_param_ref_mut!(signer).as_mut()
+ }
+ ).collect();
+ fry_box!(ctx, Signer::detached(*inner, signers))
}
/// Writes a literal data packet.
diff --git a/openpgp/Cargo.toml b/openpgp/Cargo.toml
index 5909a0d4..09d566b1 100644
--- a/openpgp/Cargo.toml
+++ b/openpgp/Cargo.toml
@@ -36,6 +36,9 @@ time = "0.1.40"
[build-dependencies]
lalrpop = "0.16"
+[dev-dependencies]
+rpassword = "2.0.0"
+
[features]
default = ["compression"]
diff --git a/openpgp/examples/notarize.rs b/openpgp/examples/notarize.rs
index 49cb2069..5214ad9e 100644
--- a/openpgp/examples/notarize.rs
+++ b/openpgp/examples/notarize.rs
@@ -7,8 +7,10 @@ use std::io;
extern crate sequoia_openpgp as openpgp;
use openpgp::{
armor,
+ crypto,
Packet,
constants::DataFormat,
+ packet::key::SecretKey,
parse::{Parse, PacketParserResult},
serialize::Serialize,
};
@@ -23,10 +25,33 @@ fn main() {
}
// Read the transferable secret keys from the given files.
- let tsks: Vec<openpgp::TPK> = args[1..].iter().map(|f| {
- openpgp::TPK::from_file(f)
- .expect("Failed to read key")
- }).collect();
+ let mut keys = Vec::new();
+ 'nextfile: for filename in &args[1..] {
+ let tsk = openpgp::TPK::from_file(filename)
+ .expect("Failed to read key");
+
+ 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 'nextfile;
+ }
+ }
+
+ panic!("Found no suitable signing key on {}", tsk);
+ }
// Compose a writer stack corresponding to the output format and
// packet structure we want. First, we want the output to be
@@ -39,7 +64,8 @@ fn main() {
// Now, create a signer that emits a detached signature.
let mut signer = Signer::new(
- message, &tsks.iter().collect::<Vec<&openpgp::TPK>>())
+ message,
+ keys.iter_mut().map(|s| -> &mut dyn crypto::Signer { s }).collect())
.expect("Failed to create signer");
// Create a parser for the message to be notarized.
diff --git a/openpgp/examples/sign-detached.rs b/openpgp/examples/sign-detached.rs
index 46c0df1d..19dc771f 100644
--- a/openpgp/examples/sign-detached.rs
+++ b/openpgp/examples/sign-detached.rs
@@ -2,9 +2,12 @@
use std::env;
use std::io;
+extern crate rpassword;
extern crate sequoia_openpgp as openpgp;
use openpgp::armor;
+use openpgp::crypto;
+use openpgp::packet::key::SecretKey;
use openpgp::parse::Parse;
use openpgp::serialize::stream::{Message, Signer};
@@ -17,10 +20,33 @@ fn main() {
}
// Read the transferable secret keys from the given files.
- let tsks: Vec<openpgp::TPK> = args[1..].iter().map(|f| {
- openpgp::TPK::from_file(f)
- .expect("Failed to read key")
- }).collect();
+ let mut keys = Vec::new();
+ 'nextfile: for filename in &args[1..] {
+ let tsk = openpgp::TPK::from_file(filename)
+ .expect("Failed to read key");
+
+ 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 'nextfile;
+ }
+ }
+
+ panic!("Found no suitable signing key on {}", tsk);
+ }
// Compose a writer stack corresponding to the output format and
// packet structure we want. First, we want the output to be
@@ -33,7 +59,8 @@ fn main() {
// Now, create a signer that emits a detached signature.
let mut signer = Signer::detached(
- message, &tsks.iter().collect::<Vec<&openpgp::TPK>>())
+ message,
+ keys.iter_mut().map(|s| -> &mut dyn crypto::Signer { s }).collect())
.expect("Failed to create signer");
// Copy all the data.
diff --git a/openpgp/examples/sign.rs b/openpgp/examples/sign.rs
index 961c9597..c3ee0728 100644
--- a/openpgp/examples/sign.rs
+++ b/openpgp/examples/sign.rs
@@ -5,7 +5,9 @@ use std::io;
extern crate sequoia_openpgp as openpgp;
use openpgp::armor;
+use openpgp::crypto;
use openpgp::constants::DataFormat;
+use openpgp::packet::key::SecretKey;
use openpgp::parse::Parse;
use openpgp::serialize::stream::{Message, LiteralWriter, Signer};
@@ -18,10 +20,33 @@ fn main() {
}
// Read the transferable secret keys from the given files.
- let tsks: Vec<openpgp::TPK> = args[1..].iter().map(|f| {
- openpgp::TPK::from_file(f)
- .expect("Failed to read key")
- }).collect();
+ let mut keys = Vec::new();
+ 'nextfile: for filename in &args[1..] {
+ let tsk = openpgp::TPK::from_file(filename)
+ .expect("Failed to read key");
+
+ 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 'nextfile;
+ }
+ }
+
+ panic!("Found no suitable signing key on {}", tsk);
+ }
// Compose a writer stack corresponding to the output format and
// packet structure we want. First, we want the output to be
@@ -34,7 +59,8 @@ fn main() {
// Now, create a signer that emits a signature.
let signer = Signer::new(
- message, &tsks.iter().collect::<Vec<&openpgp::TPK>>())
+ message,
+ keys.iter_mut().map(|s| -> &mut dyn crypto::Signer { s }).collect())
.expect("Failed to create signer");
// Then, create a literal writer to wrap the data in a literal
diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs
index 1d2fe4eb..82e0d3ad 100644
--- a/openpgp/src/parse/stream.rs
+++ b/openpgp/src/parse/stream.rs
@@ -1479,6 +1479,8 @@ mod test {
use constants::DataFormat;
use tpk::{TPKBuilder, CipherSuite};
use serialize::stream::{LiteralWriter, Signer, Message};
+ use packet::key::SecretKey;
+ use crypto::KeyPair;
use std::io::Write;
let (tpk, _) = TPKBuilder::default()
@@ -1489,8 +1491,15 @@ mod test {
// sign 30MiB message
let mut buf = vec![];
{
+ let key = tpk.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 m = Message::new(&mut buf);
- let signer = Signer::new(m, &[&tpk]).unwrap();
+ let signer = Signer::new(m, vec![&mut keypair]).unwrap();
let mut ls = LiteralWriter::new(signer, DataFormat::Binary, None, None).unwrap();
ls.write_all(&mut vec![42u8; 30 * 1024 * 1024]).unwrap();
diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs
index 503ad11c..ecb8e166 100644
--- a/openpgp/src/serialize/stream.rs
+++ b/openpgp/src/serialize/stream.rs
@@ -16,7 +16,7 @@ use time;
use nettle::{Hash, Yarrow};
use {
- crypto::KeyPair,
+ crypto,
Error,
Fingerprint,
HashAlgorithm,
@@ -28,7 +28,6 @@ use {
packet::PKESK,
Result,
crypto::Password,
- packet::key::SecretKey,
crypto::SessionKey,
packet::SKESK4,
packet::SKESK5,
@@ -205,7 +204,7 @@ pub struct Signer<'a> {
// take our inner reader. If that happens, we only update the
// digests.
inner: Option<writer::BoxStack<'a, Cookie>>,
- keys: Vec<&'a Key>,
+ signers: Vec<&'a mut dyn crypto::Signer>,
intended_recipients: Option<Vec<Fingerprint>>,
detached: bool,
hash: Box<Hash>,
@@ -223,17 +222,25 @@ impl<'a> Signer<'a> {
/// use openpgp::constants::DataFormat;
/// use openpgp::serialize::stream::{Message, Signer, LiteralWriter};
/// # use openpgp::{Result, TPK};
+ /// # use openpgp::packet::key::SecretKey;
+ /// # use openpgp::crypto::KeyPair;
/// # use openpgp::parse::Parse;
/// # let tsk = TPK::from_bytes(include_bytes!(
/// # "../../tests/data/keys/testy-new-private.pgp"))
/// # .unwrap();
- /// # f(tsk).unwrap();
- /// # fn f(tsk: TPK) -> Result<()> {
+ /// # let key = tsk.select_signing_keys(None)[0];
+ /// # let sec = match key.secret() {
+ /// # Some(SecretKey::Unencrypted { ref mpis }) => mpis,
+ /// # _ => unreachable!(),
+ /// # };
+ /// # let keypair = KeyPair::new(key.clone(), sec.clone()).unwrap();
+ /// # f(keypair).unwrap();
+ /// # fn f(mut signing_keypair: KeyPair) -> Result<()> {
///
/// let mut o = vec![];
/// {
/// let message = Message::new(&mut o);
- /// let signer = Signer::new(message, &[&tsk])?;
+ /// let signer = Signer::new(message, vec![&mut signing_keypair])?;
/// let mut ls = LiteralWriter::new(signer, DataFormat::Text, None, None)?;
/// ls.write_all(b"Make it so, number one!")?;
/// ls.finalize()?;
@@ -241,7 +248,8 @@ impl<'a> Signer<'a> {
/// # Ok(())
/// # }
/// ```
- pub fn new(inner: writer::Stack<'a, Cookie>, signers: &[&'a TPK])
+ pub fn new(inner: writer::Stack<'a, Cookie>,
+ signers: Vec<&'a mut dyn crypto::Signer>)
-> Result<writer::Stack<'a, Cookie>> {
Self::make(inner, signers, None, false)
}
@@ -253,7 +261,7 @@ impl<'a> Signer<'a> {
/// signature. This prevents forwarding a signed message using a
/// different encryption context.
pub fn with_intended_recipients(inner: writer::Stack<'a, Cookie>,
- signers: &[&'a TPK],
+ signers: Vec<&'a mut dyn crypto::Signer>,
recipients: &[&'a TPK])
-> Result<writer::Stack<'a, Cookie>> {
Self::make(inner, signers,
@@ -270,17 +278,25 @@ impl<'a> Signer<'a> {
/// use std::io::Write;
/// use openpgp::serialize::stream::{Message, Signer, LiteralWriter};
/// # use openpgp::{Result, TPK};
+ /// # use openpgp::packet::key::SecretKey;
+ /// # use openpgp::crypto::KeyPair;
/// # use openpgp::parse::Parse;
/// # let tsk = TPK::from_bytes(include_bytes!(
/// # "../../tests/data/keys/testy-new-private.pgp"))
/// # .unwrap();
- /// # f(tsk).unwrap();
- /// # fn f(tsk: TPK) -> Result<()> {
+ /// # let key = tsk.select_signing_keys(None)[0];
+ /// # let sec = match key.secret() {
+ /// # Some(SecretKey::Unencrypted { ref mpis }) => mpis,
+ /// # _ => unreachable!(),
+ /// # };
+ /// # let keypair = KeyPair::new(key.clone(), sec.clone()).unwrap();
+ /// # f(keypair).unwrap();
+ /// # fn f(mut signing_keypair: KeyPair) -> Result<()> {
///
/// let mut o = vec![];
/// {
/// let message = Message::new(&mut o);
- /// let mut signer = Signer::detached(message, &[&tsk])?;
+ /// let mut signer = Signer::detached(message, vec![&mut signing_keypair])?;
/// signer.write_all(b"Make it so, number one!")?;
/// // In reality, just io::copy() the file to be signed.
/// signer.finalize()?;
@@ -288,88 +304,35 @@ impl<'a> Signer<'a> {
/// # Ok(())
/// # }
/// ```
- pub fn detached(inner: writer::Stack<'a, Cookie>, signers: &[&'a TPK])
+ pub fn detached(inner: writer::Stack<'a, Cookie>,
+ signers: Vec<&'a mut dyn crypto::Signer>)
-> Result<writer::Stack<'a, Cookie>> {
Self::make(inner, signers, None, true)
}
- fn make(inner: writer::Stack<'a, Cookie>, signers: &[&'a TPK],
+ fn make(inner: writer::Stack<'a, Cookie>,
+ signers: Vec<&'a mut dyn crypto::Signer>,
intended_recipients: Option<Vec<Fingerprint>>, detached: bool)
-> Result<writer::Stack<'a, Cookie>> {
let mut inner = writer::BoxStack::from(inner);
// Just always use SHA512.
let hash_algo = HashAlgorithm::SHA512;
- let mut signing_keys = Vec::new();
if signers.len() == 0 {
return Err(Error::InvalidArgument(
"No signing keys given".into()).into());
}
- for tsk in signers {
- // We need to find all (sub)keys capable of signing.
- let can_sign = |key: &Key, sig: Option<&Signature>| -> bool {
- if let Some(sig) = sig {
- sig.key_flags().can_sign()
- // Check expiry.
- && sig.signature_alive()
- && sig.key_alive(key)
- } else {
- false
- }
- };
-
- // Gather all signing-capable subkeys.
- let subkeys = tsk.subkeys().filter_map(|skb| {
- let key = skb.subkey();
- if can_sign(key, skb.binding_signature()) {
- Some(key)
- } else {
- None
- }
- });
-
- // Check if the primary key is signing-capable.
- let primary_can_sign =
- can_sign(tsk.primary(), tsk.primary_key_signature());
-
- // If the primary key is signing-capable, prepend to
- // subkeys via iterator magic.
- let keys =
- iter::once(tsk.primary())
- .filter(|_| primary_can_sign)
- .chain(subkeys);
-
- // Check that we found at least one per TSK.
- let mut found_secret = false;
-
- // For every suitable key, check if we have a secret key.
- for key in keys {
- if let Some(ref secret) = key.secret() {
- if let &SecretKey::Unencrypted { .. } = secret {
- // Success!
- signing_keys.push(key);
- found_secret = true;
- }
- }
- }
-
- if ! found_secret {
- return Err(Error::InvalidArgument(
- format!("Key {} has no signing-capable secret key", tsk))
- .into());
- }
- }
-
if ! detached {
// For every key we collected, build and emit a one pass
// signature packet.
- for (i, key) in signing_keys.iter().enumerate() {
+ for (i, keypair) in signers.iter().enumerate() {
+ let key = keypair.public();
let mut ops = OnePassSig::new(SignatureType::Binary);
ops.set_pk_algo(key.pk_algo());
ops.set_hash_algo(hash_algo);
ops.set_issuer(key.fingerprint().to_keyid());
- ops.set_last(i == signing_keys.len() - 1);
+ ops.set_last(i == signers.len() - 1);
ops.serialize(&mut inner)?;
}
}
@@ -377,7 +340,7 @@ impl<'a> Signer<'a> {
let level = inner.cookie_ref().level + 1;
Ok(writer::Stack::from(Box::new(Signer {
inner: Some(inner),
- keys: signing_keys,
+ signers: signers,
intended_recipients: intended_recipients,
detached: detached,
hash: hash_algo.context()?,
@@ -393,7 +356,7 @@ impl<'a> Signer<'a> {
// Emit the signatures in reverse, so that the
// one-pass-signature and signature packets "bracket" the
// message.
- while let Some(key) = self.keys.pop() {
+ for signer in self.signers.iter_mut() {
// Part of the signature packet is hashed in,
// therefore we need to clone the hash.
let mut hash = self.hash.clone();
@@ -401,24 +364,17 @@ impl<'a> Signer<'a> {
// Make and hash a signature packet.
let mut sig = signature::Builder::new(SignatureType::Binary);
sig.set_signature_creation_time(time::now().canonicalize())?;
- sig.set_issuer_fingerprint(key.fingerprint())?;
+ sig.set_issuer_fingerprint(signer.public().fingerprint())?;
// GnuPG up to (and including) 2.2.8 requires the
// Issuer subpacket to be present.
- sig.set_issuer(key.keyid())?;
+ sig.set_issuer(signer.public().keyid())?;
if let Some(ref ir) = self.intended_recipients {
sig.set_intended_recipients(ir.clone())?;
}
// Compute the signature.
- let sig = if let &SecretKey::Unencrypted { mpis: ref sec } =
- key.secret().expect("validated in constructor")
- {
- sig.sign_hash(&mut KeyPair::new(key.clone(), sec.clone())?,
- HashAlgorithm::SHA512, hash)?
- } else {
- panic!("validated in constructor");
- };
+ let sig = sig.sign_hash(*signer, HashAlgorithm::SHA512, hash)?;
// And emit the packet.
sig.serialize(sink)?;
@@ -1325,21 +1281,41 @@ mod test {
#[test]
fn signature() {
+ use crypto::KeyPair;
+ use packet::KeyFlags;
+ use packet::key::SecretKey;
use std::collections::HashMap;
use Fingerprint;
- let mut tsks: HashMap<Fingerprint, TPK> = HashMap::new();
- let tsk = TPK::from_bytes(bytes!("keys/testy-private.pgp")).unwrap();
- tsks.insert(tsk.fingerprint(), tsk);
- let tsk = TPK::from_bytes(bytes!("keys/testy-new-private.pgp")).unwrap();
- tsks.insert(tsk.fingerprint(), tsk);
+ let mut keys: HashMap<Fingerprint, Key> = HashMap::new();
+ for tsk in &[
+ TPK::from_bytes(bytes!("keys/testy-private.pgp")).unwrap(),
+ TPK::from_bytes(bytes!("keys/testy-new-private.pgp")).unwrap(),
+ ] {
+ for key in tsk.select_keys(
+ KeyFlags::default().set_sign(true), None)
+ {
+ keys.insert(key.fingerprint(), key.clone());
+ }
+ }
let mut o = vec![];
{
+ let mut signers = keys.iter().map(|(_, key)| {
+ match key.secret() {
+ Some(SecretKey::Unencrypted { ref mpis }) =>
+ KeyPair::new(key.clone(), mpis.clone()).unwrap(),
+ s =>
+ panic!("expected unencrypted secret key, got: {:?}", s),
+ }
+ }).collect::<Vec<KeyPair>>();
+
let m = Message::new(&mut o);
let signer = Signer::new(
m,
- &tsks.iter().map(|(_, tsk)| tsk).collect::<Vec<&TPK>>())
+ signers.iter_mut()
+ .map(|s| -> &mut dyn crypto::Signer {s})
+ .collect())
.unwrap();
let mut ls = LiteralWriter::new(signer, T, None, None).unwrap();
ls.write_all(b"Tis, tis, tis. Tis is important.").unwrap();
@@ -1351,9 +1327,9 @@ mod test {
let mut good = 0;
while let PacketParserResult::Some(pp) = ppr {
if let Packet::Signature(ref sig) = pp.packet {
- let tpk = tsks.get(&sig.issuer_fingerprint().unwrap())
+ let key = keys.get(&sig.issuer_fingerprint().unwrap())
.unwrap();
- let result = sig.verify(tpk.primary()).unwrap();
+ let result = sig.verify(key).unwrap();
assert!(result);
good += 1;
}
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,
+ signe