diff options
29 files changed, 292 insertions, 95 deletions
diff --git a/guide/src/chapter_01.md b/guide/src/chapter_01.md index b4a4ccab..6368a9eb 100644 --- a/guide/src/chapter_01.md +++ b/guide/src/chapter_01.md @@ -51,8 +51,8 @@ fn main() { # fn sign(sink: &mut Write, plaintext: &str, tsk: &openpgp::Cert) # -> openpgp::Result<()> { # // Get the keypair to do the signing from the Cert. -# let keypair = tsk.keys_valid().for_signing().nth(0).unwrap().2 -# .clone().mark_parts_secret().unwrap().into_keypair()?; +# let keypair = tsk.keys_valid().for_signing().nth(0).unwrap(). +# key().clone().mark_parts_secret().unwrap().into_keypair()?; # # // Start streaming an OpenPGP message. # let message = Message::new(sink); @@ -196,8 +196,8 @@ fn generate() -> openpgp::Result<openpgp::Cert> { # fn sign(sink: &mut Write, plaintext: &str, tsk: &openpgp::Cert) # -> openpgp::Result<()> { # // Get the keypair to do the signing from the Cert. -# let keypair = tsk.keys_valid().for_signing().nth(0).unwrap().2 -# .clone().mark_parts_secret().unwrap().into_keypair()?; +# let keypair = tsk.keys_valid().for_signing().nth(0).unwrap(). +# key().clone().mark_parts_secret().unwrap().into_keypair()?; # # // Start streaming an OpenPGP message. # let message = Message::new(sink); @@ -341,8 +341,8 @@ implements [`io::Write`], and we simply write the plaintext to it. fn sign(sink: &mut Write, plaintext: &str, tsk: &openpgp::Cert) -> openpgp::Result<()> { // Get the keypair to do the signing from the Cert. - let keypair = tsk.keys_valid().for_signing().nth(0).unwrap().2 - .clone().mark_parts_secret().unwrap().into_keypair()?; + let keypair = tsk.keys_valid().for_signing().nth(0).unwrap(). + key().clone().mark_parts_secret().unwrap().into_keypair()?; // Start streaming an OpenPGP message. let message = Message::new(sink); @@ -497,8 +497,8 @@ Verified data can be read from this using [`io::Read`]. # fn sign(sink: &mut Write, plaintext: &str, tsk: &openpgp::Cert) # -> openpgp::Result<()> { # // Get the keypair to do the signing from the Cert. -# let keypair = tsk.keys_valid().for_signing().nth(0).unwrap().2 -# .clone().mark_parts_secret().unwrap().into_keypair()?; +# let keypair = tsk.keys_valid().for_signing().nth(0).unwrap(). +# key().clone().mark_parts_secret().unwrap().into_keypair()?; # # // Start streaming an OpenPGP message. # let message = Message::new(sink); diff --git a/guide/src/chapter_02.md b/guide/src/chapter_02.md index cc024348..e1687803 100644 --- a/guide/src/chapter_02.md +++ b/guide/src/chapter_02.md @@ -53,7 +53,7 @@ fn main() { # let mut recipients = # recipient.keys_valid() # .for_transport_encryption() -# .map(|(_, _, key)| key.into()) +# .map(|ka| ka.key().into()) # .collect::<Vec<_>>(); # # // Start streaming an OpenPGP message. @@ -194,7 +194,7 @@ fn generate() -> openpgp::Result<openpgp::Cert> { # let mut recipients = # recipient.keys_valid() # .for_transport_encryption() -# .map(|(_, _, key)| key.into()) +# .map(|ka| ka.key().into()) # .collect::<Vec<_>>(); # # // Start streaming an OpenPGP message. @@ -335,7 +335,7 @@ fn encrypt(sink: &mut Write, plaintext: &str, recipient: &openpgp::Cert) let mut recipients = recipient.keys_valid() .for_transport_encryption() - .map(|(_, _, key)| key.into()) + .map(|ka| ka.key().into()) .collect::<Vec<_>>(); // Start streaming an OpenPGP message. @@ -490,7 +490,7 @@ Decrypted data can be read from this using [`io::Read`]. # let mut recipients = # recipient.keys_valid() # .for_transport_encryption() -# .map(|(_, _, key)| key.into()) +# .map(|ka| ka.key().into()) # .collect::<Vec<_>>(); # # // Start streaming an OpenPGP message. diff --git a/ipc/examples/gpg-agent-decrypt.rs b/ipc/examples/gpg-agent-decrypt.rs index f59912a5..381bab6d 100644 --- a/ipc/examples/gpg-agent-decrypt.rs +++ b/ipc/examples/gpg-agent-decrypt.rs @@ -74,11 +74,13 @@ impl<'a> Helper<'a> { // Map (sub)KeyIDs to secrets. let mut keys = HashMap::new(); for cert in certs { - for (sig, _, key) in cert.keys_all() { - if sig.map(|s| (s.key_flags().for_storage_encryption() - || s.key_flags().for_transport_encryption())) + for ka in cert.keys_all() { + if ka.binding_signature(None) + .map(|s| (s.key_flags().for_storage_encryption() + || s.key_flags().for_transport_encryption())) .unwrap_or(false) { + let key = ka.key(); keys.insert(key.keyid(), key.clone().into()); } } diff --git a/ipc/examples/gpg-agent-sign.rs b/ipc/examples/gpg-agent-sign.rs index e0cfeb98..dfc3f304 100644 --- a/ipc/examples/gpg-agent-sign.rs +++ b/ipc/examples/gpg-agent-sign.rs @@ -39,8 +39,8 @@ fn main() { // Construct a KeyPair for every signing-capable (sub)key. let mut signers = certs.iter().flat_map(|cert| { - cert.keys_valid().for_signing().filter_map(|(_, _, key)| { - KeyPair::new(&ctx, key).ok() + cert.keys_valid().for_signing().filter_map(|ka| { + KeyPair::new(&ctx, ka.key()).ok() }) }).collect::<Vec<KeyPair>>(); diff --git a/ipc/tests/gpg-agent.rs b/ipc/tests/gpg-agent.rs index c284117f..6a74ffa3 100644 --- a/ipc/tests/gpg-agent.rs +++ b/ipc/tests/gpg-agent.rs @@ -95,7 +95,7 @@ fn sign() { gpg_import(&ctx, &buf); let keypair = KeyPair::new( - &ctx, cert.keys_valid().for_signing().take(1).next().unwrap().2) + &ctx, cert.keys_valid().for_signing().take(1).next().unwrap().key()) .unwrap(); let mut message = Vec::new(); @@ -210,7 +210,7 @@ fn decrypt() { let recipient = cert.keys_valid().key_flags( KeyFlags::default().set_transport_encryption(true)) - .map(|(_, _, key)| key.into()) + .map(|ka| ka.key().into()) .nth(0).unwrap(); // Start streaming an OpenPGP message. @@ -277,7 +277,7 @@ fn decrypt() { self.ctx, self.cert.keys_valid().key_flags( KeyFlags::default().set_transport_encryption(true)) - .take(1).next().unwrap().2) + .take(1).next().unwrap().key()) .unwrap(); pkesks[0].decrypt(&mut keypair) diff --git a/net/src/lib.rs b/net/src/lib.rs index e0b94434..a7746727 100644 --- a/net/src/lib.rs +++ b/net/src/lib.rs @@ -183,8 +183,8 @@ impl KeyServer { Some(armor::Kind::PublicKey))); match Cert::from_reader(r) { Ok(cert) => { - if cert.keys_all().any(|(_, _, key)| { - KeyID::from(key.fingerprint()) + if cert.keys_all().any(|ka| { + KeyID::from(ka.key().fingerprint()) == keyid_want }) { future::done(Ok(cert)) diff --git a/openpgp-ffi/src/cert.rs b/openpgp-ffi/src/cert.rs index b81150fa..2791ad1c 100644 --- a/openpgp-ffi/src/cert.rs +++ b/openpgp-ffi/src/cert.rs @@ -695,7 +695,12 @@ pub extern "C" fn pgp_cert_key_iter_next<'a>( let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); iter_wrapper.next_called = true; - if let Some((sig, rs, key)) = iter_wrapper.iter.next() { + if let Some(ka) = iter_wrapper.iter.next() { + // XXX: Shouldn't assume the current time. + let sig = ka.binding_signature(None); + let rs = ka.revoked(None); + let key = ka.key(); + if let Some(ptr) = sigo { *ptr = sig.move_into_raw(); } diff --git a/openpgp-ffi/src/serialize.rs b/openpgp-ffi/src/serialize.rs index 6f24a5aa..0a6435a0 100644 --- a/openpgp-ffi/src/serialize.rs +++ b/openpgp-ffi/src/serialize.rs @@ -282,7 +282,7 @@ fn pgp_recipients_from_key_iter<'a>( let result_len = ffi_param_ref_mut!(result_len); let recipients = iter_wrapper.iter - .map(|(_, _, key)| key.into()) + .map(|ka| ka.key().into()) .collect::<Vec<openpgp::serialize::stream::Recipient>>(); let result = unsafe { diff --git a/openpgp/examples/decrypt-with.rs b/openpgp/examples/decrypt-with.rs index 224a8726..7c924cc9 100644 --- a/openpgp/examples/decrypt-with.rs +++ b/openpgp/examples/decrypt-with.rs @@ -58,16 +58,17 @@ impl Helper { // Map (sub)KeyIDs to secrets. let mut keys = HashMap::new(); for cert in certs { - for (sig, _, key) in cert.keys_all() { - if sig.map(|s| (s.key_flags().for_storage_encryption() - || s.key_flags().for_transport_encryption())) + for ka in cert.keys_all() { + if ka.binding_signature(None) + .map(|s| (s.key_flags().for_storage_encryption() + || s.key_flags().for_transport_encryption())) .unwrap_or(false) { // This only works for unencrypted secret keys. if let Ok(keypair) = - key.clone().mark_parts_secret().unwrap().into_keypair() + ka.key().clone().mark_parts_secret().unwrap().into_keypair() { - keys.insert(key.keyid(), keypair); + keys.insert(ka.key().keyid(), keypair); } } } diff --git a/openpgp/examples/encrypt-for.rs b/openpgp/examples/encrypt-for.rs index f3004532..eceec5b3 100644 --- a/openpgp/examples/encrypt-for.rs +++ b/openpgp/examples/encrypt-for.rs @@ -38,7 +38,7 @@ fn main() { let mut recipients = certs.iter() .flat_map(|cert| cert.keys_valid().key_flags(mode.clone())) - .map(|(_, _, key)| key.into()) + .map(|ka| ka.key().into()) .collect::<Vec<_>>(); // Compose a writer stack corresponding to the output format and diff --git a/openpgp/examples/generate-encrypt-decrypt.rs b/openpgp/examples/generate-encrypt-decrypt.rs index 55ea89d6..01782b47 100644 --- a/openpgp/examples/generate-encrypt-decrypt.rs +++ b/openpgp/examples/generate-encrypt-decrypt.rs @@ -44,7 +44,7 @@ fn encrypt(sink: &mut dyn Write, plaintext: &str, recipient: &openpgp::Cert) let mut recipients = recipient.keys_valid() .for_transport_encryption() - .map(|(_, _, key)| key.into()) + .map(|ka| ka.key().into()) .collect::<Vec<_>>(); // Start streaming an OpenPGP message. diff --git a/openpgp/examples/generate-sign-verify.rs b/openpgp/examples/generate-sign-verify.rs index 6e3d8084..1cccac44 100644 --- a/openpgp/examples/generate-sign-verify.rs +++ b/openpgp/examples/generate-sign-verify.rs @@ -40,8 +40,8 @@ fn generate() -> openpgp::Result<openpgp::Cert> { fn sign(sink: &mut dyn Write, plaintext: &str, tsk: &openpgp::Cert) -> openpgp::Result<()> { // Get the keypair to do the signing from the Cert. - let keypair = tsk.keys_valid().for_signing().nth(0).unwrap().2 - .clone().mark_parts_secret().unwrap().into_keypair()?; + let keypair = tsk.keys_valid().for_signing().nth(0).unwrap() + .key().clone().mark_parts_secret().unwrap().into_keypair()?; // Start streaming an OpenPGP message. let message = Message::new(sink); diff --git a/openpgp/examples/notarize.rs b/openpgp/examples/notarize.rs index ce007968..f52466f4 100644 --- a/openpgp/examples/notarize.rs +++ b/openpgp/examples/notarize.rs @@ -28,7 +28,7 @@ fn main() { .expect("Failed to read key"); let mut n = 0; - for (_, _, key) in tsk.keys_valid().for_signing().secret() { + for key in tsk.keys_valid().for_signing().secret().map(|ka| ka.key()) { keys.push({ let mut key = key.clone(); if key.secret().expect("filtered").is_encrypted() { diff --git a/openpgp/examples/pad.rs b/openpgp/examples/pad.rs index a13e53d5..32583a42 100644 --- a/openpgp/examples/pad.rs +++ b/openpgp/examples/pad.rs @@ -40,7 +40,7 @@ fn main() { let mut recipients = certs.iter() .flat_map(|cert| cert.keys_valid().key_flags(mode.clone())) - .map(|(_, _, key)| Recipient::new(KeyID::wildcard(), key)) + .map(|ka| Recipient::new(KeyID::wildcard(), ka.key())) .collect::<Vec<_>>(); // Compose a writer stack corresponding to the output format and diff --git a/openpgp/examples/sign-detached.rs b/openpgp/examples/sign-detached.rs index 0fee0e36..20d4150b 100644 --- a/openpgp/examples/sign-detached.rs +++ b/openpgp/examples/sign-detached.rs @@ -24,7 +24,7 @@ fn main() { .expect("Failed to read key"); let mut n = 0; - for (_, _, key) in tsk.keys_valid().for_signing().secret() { + for key in tsk.keys_valid().for_signing().secret().map(|ka| ka.key()) { keys.push({ let mut key = key.clone(); if key.secret().expect("filtered").is_encrypted() { diff --git a/openpgp/examples/sign.rs b/openpgp/examples/sign.rs index 195ac508..7fae29ef 100644 --- a/openpgp/examples/sign.rs +++ b/openpgp/examples/sign.rs @@ -23,7 +23,7 @@ fn main() { .expect("Failed to read key"); let mut n = 0; - for (_, _, key) in tsk.keys_valid().for_signing().secret() { + for key in tsk.keys_valid().for_signing().secret().map(|ka| ka.key()) { keys.push({ let mut key = key.clone(); if key.secret().expect("filtered").is_encrypted() { diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs index ce1a0a6c..24bc5fe7 100644 --- a/openpgp/src/cert/builder.rs +++ b/openpgp/src/cert/builder.rs @@ -656,13 +656,17 @@ mod tests { assert!(! sig.key_alive(key, now + 610 * s).is_ok()); let (sig, key) = cert.keys_valid().for_signing() - .nth(0).map(|(s, _, k)| (s.unwrap(), k)).unwrap(); + .nth(0).map(|ka| { + (ka.binding_signature(None).unwrap(), ka.key()) + }).unwrap(); assert!(sig.key_alive(key, now).is_ok()); assert!(sig.key_alive(key, now + 290 * s).is_ok()); assert!(! sig.key_alive(key, now + 310 * s).is_ok()); let (sig, key) = cert.keys_valid().for_authentication() - .nth(0).map(|(s, _, k)| (s.unwrap(), k)).unwrap(); + .nth(0).map(|ka| { + (ka.binding_signature(None).unwrap(), ka.key()) + }).unwrap(); assert!(sig.key_alive(key, now).is_ok()); assert!(sig.key_alive(key, now + 590 * s).is_ok()); assert!(! sig.key_alive(key, now + 610 * s).is_ok()); diff --git a/openpgp/src/cert/key_amalgamation.rs b/openpgp/src/cert/key_amalgamation.rs new file mode 100644 index 00000000..af7f7a7e --- /dev/null +++ b/openpgp/src/cert/key_amalgamation.rs @@ -0,0 +1,183 @@ +use std::time; +use std::convert::TryInto; +use std::convert::TryFrom; + +use crate::{ + Cert, + cert::KeyBinding, + packet::key, + packet::Key, + packet::Signature, + Result, + RevocationStatus, +}; + +/// A variant of `KeyAmalgamation` for primary keys. +#[derive(Debug)] +struct PrimaryKeyAmalgamation<'a, P: key::KeyParts> { + cert: &'a Cert, + binding: &'a KeyBinding<P, key::PrimaryRole>, +} + +/// A variant of `KeyAmalgamation` for subkeys. +#[derive(Debug)] +struct SubordinateKeyAmalgamation<'a, P: key::KeyParts> { + cert: &'a Cert, + binding: &'a KeyBinding<P, key::SubordinateRole>, +} + +/// The underlying `KeyAmalgamation` type. +/// +/// We don't make this type public, because an enum's variant types +/// must also all be public, and we don't want that here. Wrapping +/// this in a struct means that we can hide that. +#[derive(Debug)] +enum KeyAmalgamation0<'a, P: key::KeyParts> { + Primary(PrimaryKeyAmalgamation<'a, P>), + Subordinate(SubordinateKeyAmalgamation<'a, P>), +} + +/// A `Key` and its associated data. +#[derive(Debug)] +pub struct KeyAmalgamation<'a, P: key::KeyParts>(KeyAmalgamation0<'a, P>); + +impl<'a, P> From<(&'a Cert, &'a KeyBinding<P, key::PrimaryRole>)> + for KeyAmalgamation<'a, P> + where P: key::KeyParts +{ + fn from(x: (&'a Cert, &'a KeyBinding<P, key::PrimaryRole>)) -> Self { + KeyAmalgamation(KeyAmalgamation0::Primary(PrimaryKeyAmalgamation { + cert: x.0, + binding: x.1, + })) + } +} + +impl<'a, P> From<(&'a Cert, &'a KeyBinding<P, key::SubordinateRole>)> + for KeyAmalgamation<'a, P> + where P: key::KeyParts +{ + fn from(x: (&'a Cert, &'a KeyBinding<P, key::SubordinateRole>)) -> Self { + KeyAmalgamation(KeyAmalgamation0::Subordinate(SubordinateKeyAmalgamation { + cert: x.0, + binding: x.1, + })) + } +} + +// We can't make the key parts generic, because then the impl would +// conflict with 'impl<T> std::convert::From<T> for T'. +impl<'a> From<KeyAmalgamation<'a, key::PublicParts>> + for KeyAmalgamation<'a, key::UnspecifiedParts> +{ + fn from(ka: KeyAmalgamation<'a, key::PublicParts>) -> Self { + match ka { + KeyAmalgamation(KeyAmalgamation0::Primary(ka)) => { + KeyAmalgamation(KeyAmalgamation0::Primary( + PrimaryKeyAmalgamation { + cert: ka.cert, + binding: ka.binding.into(), + }) + ) + } + KeyAmalgamation(KeyAmalgamation0::Subordinate(ka)) => { + KeyAmalgamation(KeyAmalgamation0::Subordinate( + SubordinateKeyAmalgamation { + cert: ka.cert, + binding: ka.binding.into(), + }) + ) + } + } + } +} + +impl<'a> From<KeyAmalgamation<'a, key::SecretParts>> + for KeyAmalgamation<'a, key::PublicParts> +{ + fn from(ka: KeyAmalgamation<'a, key::SecretParts>) -> Self { + match ka { + KeyAmalgamation(KeyAmalgamation0::Primary(ka)) => { + KeyAmalgamation(KeyAmalgamation0::Primary( + PrimaryKeyAmalgamation { + cert: ka.cert, + binding: ka.binding.into(), + }) + ) + } + KeyAmalgamation(KeyAmalgamation0::Subordinate(ka)) => { + KeyAmalgamation(KeyAmalgamation0::Subordinate( + SubordinateKeyAmalgamation { + cert: ka.cert, + binding: ka.binding.into(), + }) + ) + } + } + } +} + +impl<'a> TryFrom<KeyAmalgamation<'a, key::PublicParts>> + for KeyAmalgamation<'a, key::SecretParts> +{ + type Error = failure::Error; + + fn try_from(ka: KeyAmalgamation<'a, key::PublicParts>) -> Result<Self> { + Ok(match ka { + KeyAmalgamation(KeyAmalgamation0::Primary(ka)) => { + KeyAmalgamation(KeyAmalgamation0::Primary( + PrimaryKeyAmalgamation { + cert: ka.cert, + binding: ka.binding.try_into()?, + }) + ) + } + KeyAmalgamation(KeyAmalgamation0::Subordinate(ka)) => { + KeyAmalgamation(KeyAmalgamation0::Subordinate( + SubordinateKeyAmalgamation { + cert: ka.cert, + binding: ka.binding.try_into()?, + }) + ) + } + }) + } +} + +impl<'a, P: 'a + key::KeyParts> KeyAmalgamation<'a, P> { + /// Returns the key. + pub fn key(&self) -> &'a Key<P, key::UnspecifiedRole> + where &'a Key<P, key::UnspecifiedRole>: From<&'a key::PublicKey> + { + match self { + KeyAmalgamation(KeyAmalgamation0::Primary(ref h)) => + h.cert.primary.key().into(), + KeyAmalgamation(KeyAmalgamation0::Subordinate(ref h)) => + h.binding.key().into(), + } + } + + /// Returns the key's binding signature at time `t`, if any. + pub fn binding_signature<T>(&self, t: T) -> Option<&'a |