summaryrefslogtreecommitdiffstats
path: root/openpgp/src/cert/amalgamation/key.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/cert/amalgamation/key.rs')
-rw-r--r--openpgp/src/cert/amalgamation/key.rs1937
1 files changed, 1937 insertions, 0 deletions
diff --git a/openpgp/src/cert/amalgamation/key.rs b/openpgp/src/cert/amalgamation/key.rs
new file mode 100644
index 00000000..341dc912
--- /dev/null
+++ b/openpgp/src/cert/amalgamation/key.rs
@@ -0,0 +1,1937 @@
+//! Keys, their associated signatures, and some useful methods.
+//!
+//! A [`KeyAmalgamation`] is similar to a [`ComponentAmalgamation`],
+//! but a `KeyAmalgamation` includes some additional functionality
+//! that is needed to correctly implement a [`Key`] component's
+//! semantics. In particular, unlike other components where the
+//! binding signature stores the component's meta-data, a Primary Key
+//! doesn't have a binding signature (it is the thing that other
+//! components are bound to!), and, as a consequence, the associated
+//! meta-data is stored elsewhere.
+//!
+//! Unfortunately, a primary Key's meta-data is usually not stored on
+//! a direct key signature, which would be convenient as it is located
+//! at the same place as a binding signature would be, but on the
+//! primary User ID's binding signature. This requires some
+//! acrobatics on the implementation side to realize the correct
+//! semantics. In particular, a `Key` needs to memorize its role
+//! (i.e., whether it is a primary key or a subkey) in order to know
+//! whether to consider its own self signatures or the primary User
+//! ID's self signatures when looking for its meta-data.
+//!
+//! Ideally, a `KeyAmalgamation`'s role would be encoded in its type.
+//! This increases safety, and reduces the run-time overhead.
+//! However, we want [`Cert::keys`] to return an iterator over all
+//! keys; we don't want the user to have to specially handle the
+//! primary key when that fact is not relevant. This means that
+//! `Cert::keys` has to erase the returned `Key`s' roles: all items in
+//! an iterator must have the same type. To support this, we have to
+//! keep track of a `KeyAmalgamation`'s role at run-time.
+//!
+//! But, just because we need to erase a `KeyAmalgamation`'s role to
+//! implement `Cert::keys` doesn't mean that we have to always erase
+//! it. To achieve this, we use three data types:
+//! [`PrimaryKeyAmalgamation`], [`SubordinateKeyAmalgamation`], and
+//! [`ErasedKeyAmalgamation`]. The first two encode the role
+//! information in their type, and the last one stores it at run time.
+//! We provide conversion functions to convert the static type
+//! information into dynamic type information, and vice versa.
+//!
+//! Note: `KeyBundle`s and `KeyAmalgamation`s have a notable
+//! difference: whereas a `KeyBundle`'s role is a marker, a
+//! `KeyAmalgamation`'s role determines its semantics. A consequence
+//! of this is that it is not possible to convert a
+//! `PrimaryKeyAmalgamation` into a `SubordinateAmalgamation`s, or
+//! vice versa even though we support changing a `KeyBundle`'s role:
+//!
+//! ```
+//! # use std::convert::TryInto;
+//! # extern crate sequoia_openpgp as openpgp;
+//! # use openpgp::cert::prelude::*;
+//! # use openpgp::packet::prelude::*;
+//! # let (cert, _) = CertBuilder::new()
+//! # .add_userid("Alice")
+//! # .add_signing_subkey()
+//! # .add_transport_encryption_subkey()
+//! # .generate().unwrap();
+//! // This works:
+//! cert.primary_key().bundle().role_as_subordinate();
+//!
+//! // But this doesn't:
+//! let ka: ErasedKeyAmalgamation<_> = cert.keys().nth(0).expect("primary key");
+//! let ka: openpgp::Result<SubordinateKeyAmalgamation<key::PublicParts>> = ka.try_into();
+//! assert!(ka.is_err());
+//! ```
+//!
+//! The use of the prefix `Erased` instead of `Unspecified`
+//! (cf. [`KeyRole::UnspecifiedRole`]) emphasizes this.
+//!
+//! # Selecting Keys
+//!
+//! It is essential to choose the right keys, and to make sure that
+//! they appropriate. Below, we present some guidelines for the most
+//! common situations.
+//!
+//! ## Encrypting and Signing Messages
+//!
+//! As a general rule of thumb, when encrypting or signing a message,
+//! you want to use keys that are alive, not revoked, and have the
+//! appropriate capabilities right now. For example, the following
+//! code shows how to find a key, which is appropriate for signing a
+//! message:
+//!
+//! ```rust
+//! # extern crate sequoia_openpgp as openpgp;
+//! # use openpgp::Result;
+//! # use openpgp::cert::prelude::*;
+//! use openpgp::types::RevocationStatus;
+//! use sequoia_openpgp::policy::StandardPolicy;
+//!
+//! # fn main() { f().unwrap(); }
+//! # fn f() -> Result<()> {
+//! # let (cert, _) =
+//! # CertBuilder::general_purpose(None, Some("alice@example.org"))
+//! # .generate()?;
+//! # let mut i = 0;
+//! let p = &StandardPolicy::new();
+//!
+//! let cert = cert.with_policy(p, None)?;
+//!
+//! if let RevocationStatus::Revoked(_) = cert.revocation_status() {
+//! // The certificate is revoked, don't use any keys from it.
+//! # unreachable!();
+//! } else if let Err(_) = cert.alive() {
+//! // The certificate is not alive, don't use any keys from it.
+//! # unreachable!();
+//! } else {
+//! for ka in cert.keys() {
+//! if let RevocationStatus::Revoked(_) = ka.revocation_status() {
+//! // The key is revoked.
+//! # unreachable!();
+//! } else if let Err(_) = ka.alive() {
+//! // The key is not alive.
+//! # unreachable!();
+//! } else if ! ka.for_signing() {
+//! // The key is not signing capable.
+//! } else {
+//! // Use it!
+//! # i += 1;
+//! }
+//! }
+//! }
+//! # assert_eq!(i, 1);
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! ## Verifying a Message
+//!
+//! When verifying a message, you only want to use keys that were
+//! alive, not revoked, and signing capable *when the message was
+//! signed*. These are the keys that the signer would have used, and
+//! they reflect the signer's policy when they made the signature.
+//! (See the [`Policy` discussion] for an explanation.)
+//!
+//! For version 4 Signature packets, the `Signature Creation Time`
+//! subpacket indicates when the signature was allegedly created. For
+//! the purpose of finding the key to verify the signature, this time
+//! stamp should be trusted: if the key is authenticated and the
+//! signature is valid, then the time stamp is valid; if the signature
+//! is not valid, then forging the time stamp won't help an attacker.
+//!
+//! ```rust
+//! # extern crate sequoia_openpgp as openpgp;
+//! # use openpgp::Result;
+//! # use openpgp::cert::prelude::*;
+//! use openpgp::types::RevocationStatus;
+//! use sequoia_openpgp::policy::StandardPolicy;
+//!
+//! # fn main() { f().unwrap(); }
+//! # fn f() -> Result<()> {
+//! let p = &StandardPolicy::new();
+//!
+//! # let (cert, _) =
+//! # CertBuilder::general_purpose(None, Some("alice@example.org"))
+//! # .generate()?;
+//! # let timestamp = None;
+//! # let issuer = cert.fingerprint();
+//! # let mut i = 0;
+//! let cert = cert.with_policy(p, timestamp)?;
+//! if let RevocationStatus::Revoked(_) = cert.revocation_status() {
+//! // The certificate is revoked, don't use any keys from it.
+//! # unreachable!();
+//! } else if let Err(_) = cert.alive() {
+//! // The certificate is not alive, don't use any keys from it.
+//! # unreachable!();
+//! } else {
+//! for ka in cert.keys().key_handle(issuer) {
+//! if let RevocationStatus::Revoked(_) = ka.revocation_status() {
+//! // The key is revoked, don't use it!
+//! # unreachable!();
+//! } else if let Err(_) = ka.alive() {
+//! // The key was not alive when the signature was made!
+//! // Something fishy is going on.
+//! # unreachable!();
+//! } else if ! ka.for_signing() {
+//! // The key was not signing capable! Better be safe
+//! // than sorry.
+//! # unreachable!();
+//! } else {
+//! // Try verifying the message with this key.
+//! # i += 1;
+//! }
+//! }
+//! }
+//! # assert_eq!(i, 1);
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! ## Decrypting a Message
+//!
+//! When decrypting a message, it seems like one ought to only keys
+//! that were alive, not revoked, and encryption-capable when the
+//! message was encrypted. Unfortunately, we don't know when a
+//! message was encrypted. But anyway, due to the slow propagation of
+//! revocation certificates, we can't assume that senders won't
+//! mistakenly use a revoked key.
+//!
+//! However, wanting to decrypt a message encrypted using an expired
+//! or revoked key is reasonable. If someone is trying to decrypt a
+//! message using an expired key, then they are the certificate
+//! holder, and probably attempting to access archived data using a
+//! key that they themselves revoked! We don't want to prevent that.
+//!
+//! We do, however, want to check whether a key is really encryption
+//! capable. [This discussion] explains why using a signing key to
+//! decrypt a message can be dangerous. Since we need a binding
+//! signature to determine this, but we don't have the time that the
+//! message was encrypted, we need a workaround. One approach would
+//! be to check whether the key is encryption capable now. Since a
+//! key's key flags don't typically change, this will correctly filter
+//! out keys that are not encryption capable. But, it will skip keys
+//! whose self signature has expired. But that is not a problem
+//! either: no one sets self signatures to expire; if anything, they
+//! set keys to expire. Thus, this will not result in incorrectly
+//! failing to decrypt messages in practice, and is a reasonable
+//! approach.
+//!
+//! ```rust
+//! # extern crate sequoia_openpgp as openpgp;
+//! # use openpgp::Result;
+//! # use openpgp::cert::prelude::*;
+//! use sequoia_openpgp::policy::StandardPolicy;
+//!
+//! # fn main() { f().unwrap(); }
+//! # fn f() -> Result<()> {
+//! let p = &StandardPolicy::new();
+//!
+//! # let (cert, _) =
+//! # CertBuilder::general_purpose(None, Some("alice@example.org"))
+//! # .generate()?;
+//! let decryption_keys = cert.keys().with_policy(p, None)
+//! .for_storage_encryption().for_transport_encryption()
+//! .collect::<Vec<_>>();
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! [`KeyAmalgamation`]: struct.KeyAmalgamation.html
+//! [`ComponentAmalgamation`]: ../struct.ComponentAmalgamation.html
+//! [`Key`]: ../../../packet/key/index.html
+//! [`Cert::keys`]: ../../struct.Cert.html#method.keys
+//! [`PrimaryKeyAmalgamation`]: ../type.PrimaryKeyAmalgamation.html
+//! [`SubordinateKeyAmalgamation`]: ../type.SubordinateKeyAmalgamation.html
+//! [`ErasedKeyAmalgamation`]: ../type.ErasedKeyAmalgamation.html
+//! [`KeyRole::UnspecifiedRole`]: ../../../packet/key/trait.KeyRole.html
+//! [`Policy` discussion]: ../index.html
+//! [This discussion]: https://crypto.stackexchange.com/a/12138
+use std::time;
+use std::time::SystemTime;
+use std::ops::Deref;
+use std::borrow::Borrow;
+use std::convert::TryFrom;
+use std::convert::TryInto;
+
+use anyhow::Context;
+
+use crate::{
+ Cert,
+ cert::bundle::KeyBundle,
+ cert::amalgamation::{
+ ComponentAmalgamation,
+ ValidAmalgamation,
+ ValidateAmalgamation,
+ },
+ cert::ValidCert,
+ crypto::{hash::Hash, Signer},
+ Error,
+ packet::Key,
+ packet::key,
+ packet::signature,
+ packet::Signature,
+ policy::Policy,
+ Result,
+ SignatureType,
+ types::HashAlgorithm,
+ types::KeyFlags,
+ types::RevocationStatus,
+};
+
+mod iter;
+pub use iter::{
+ KeyAmalgamationIter,
+ ValidKeyAmalgamationIter,
+};
+
+/// Whether the key is a primary key.
+///
+/// This trait is an implementation detail. It exists so that we can
+/// have a blanket implementation of [`ValidAmalgamation`] for
+/// [`ValidKeyAmalgamation`], for instance, even though we only have
+/// specialized implementations of `PrimaryKey`.
+///
+/// [`ValidAmalgamation`]: ../trait.ValidAmalgamation.html
+/// [`ValidKeyAmalgamation`]: struct.ValidKeyAmalgamation.html
+pub trait PrimaryKey<'a, P, R>
+ where P: 'a + key::KeyParts,
+ R: 'a + key::KeyRole,
+{
+ /// Returns whether the key amalgamation is a primary key
+ /// amalgamation.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # extern crate sequoia_openpgp as openpgp;
+ /// # use openpgp::cert::prelude::*;
+ /// # use openpgp::policy::StandardPolicy;
+ /// #
+ /// # fn main() -> openpgp::Result<()> {
+ /// # let p = &StandardPolicy::new();
+ /// # let (cert, _) =
+ /// # CertBuilder::general_purpose(None, Some("alice@example.org"))
+ /// # .generate()?;
+ /// # let fpr = cert.fingerprint();
+ /// // This works if the type is concrete:
+ /// let ka: PrimaryKeyAmalgamation<_> = cert.primary_key();
+ /// assert!(ka.primary());
+ ///
+ /// // Or if it has been erased:
+ /// for (i, ka) in cert.keys().enumerate() {
+ /// let ka: ErasedKeyAmalgamation<_> = ka;
+ /// if i == 0 {
+ /// // The primary key is always the first key returned by
+ /// // `Cert::keys`.
+ /// assert!(ka.primary());
+ /// } else {
+ /// // The rest are subkeys.
+ /// assert!(! ka.primary());
+ /// }
+ /// }
+ /// # Ok(()) }
+ /// ```
+ fn primary(&self) -> bool;
+}
+
+/// A key, and its associated data, and useful methods.
+///
+/// A `KeyAmalgamation` is like a [`ComponentAmalgamation`], but
+/// specialized for keys. Due to the requirement to keep track of the
+/// key's role when it is erased ([see the module's documentation] for
+/// more details), this is a different data structure rather than a
+/// specialized type alias.
+///
+/// Generally, you won't use this type directly, but instead use
+/// [`PrimaryKeyAmalgamation`], [`SubordinateKeyAmalgamation`], or
+/// [`ErasedKeyAmalgamation`].
+///
+/// A `KeyAmalgamation` is returned by [`Cert::primary_key`], and
+/// [`Cert::keys`].
+///
+/// `KeyAmalgamation` implements [`ValidateAmalgamation`], which
+/// allows you to turn a `KeyAmalgamation` into a
+/// [`ValidKeyAmalgamation`] using [`KeyAmalgamation::with_policy`].
+///
+/// # Examples
+///
+/// Iterating over all keys:
+///
+/// ```
+/// # extern crate sequoia_openpgp as openpgp;
+/// # use openpgp::cert::prelude::*;
+/// # use openpgp::policy::StandardPolicy;
+/// #
+/// # fn main() -> openpgp::Result<()> {
+/// # let p = &StandardPolicy::new();
+/// # let (cert, _) =
+/// # CertBuilder::general_purpose(None, Some("alice@example.org"))
+/// # .generate()?;
+/// # let fpr = cert.fingerprint();
+/// for ka in cert.keys() {
+/// let ka: ErasedKeyAmalgamation<_> = ka;
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Getting the primary key:
+///
+/// ```
+/// # extern crate sequoia_openpgp as openpgp;
+/// # use openpgp::cert::prelude::*;
+/// # use openpgp::policy::StandardPolicy;
+/// #
+/// # fn main() -> openpgp::Result<()> {
+/// # let p = &StandardPolicy::new();
+/// # let (cert, _) =
+/// # CertBuilder::general_purpose(None, Some("alice@example.org"))
+/// # .generate()?;
+/// # let fpr = cert.fingerprint();
+/// let ka: PrimaryKeyAmalgamation<_> = cert.primary_key();
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Iterating over just the subkeys:
+///
+/// ```
+/// # extern crate sequoia_openpgp as openpgp;
+/// # use openpgp::cert::prelude::*;
+/// # use openpgp::policy::StandardPolicy;
+/// #
+/// # fn main() -> openpgp::Result<()> {
+/// # let p = &StandardPolicy::new();
+/// # let (cert, _) =
+/// # CertBuilder::general_purpose(None, Some("alice@example.org"))
+/// # .generate()?;
+/// # let fpr = cert.fingerprint();
+/// // We can skip the primary key (it's always first):
+/// for ka in cert.keys().skip(1) {
+/// let ka: ErasedKeyAmalgamation<_> = ka;
+/// }
+///
+/// // Or use `subkeys`, which returns a more accurate type:
+/// for ka in cert.keys().subkeys() {
+/// let ka: SubordinateKeyAmalgamation<_> = ka;
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`ComponentAmalgamation`]: ../struct.ComponentAmalgamation.html
+/// [see the module's documentation]: index.html
+/// [`PrimaryKeyAmalgamation`]: type.PrimaryKeyAmalgamation.html
+/// [`SubordinateKeyAmalgamation`]: type.SubordinateKeyAmalgamation.html
+/// [`ErasedKeyAmalgamation`]: type.ErasedKeyAmalgamation.html
+/// [`Cert::primary_key`]: ../../../cert/struct.Cert.html#method.primary_key
+/// [`Cert::keys`]: ../../../cert/struct.Cert.html#method.keys
+/// [`ValidateAmalgamation`]: ../trait.ValidateAmalgamation.html
+/// [`ValidKeyAmalgamation`]: struct.ValidKeyAmalgamation.html
+/// [`KeyAmalgamation::with_policy`]: ../trait.ValidateAmalgamation.html#method.with_policy
+#[derive(Debug)]
+pub struct KeyAmalgamation<'a, P, R, R2>
+ where P: 'a + key::KeyParts,
+ R: 'a + key::KeyRole,
+{
+ ca: ComponentAmalgamation<'a, Key<P, R>>,
+ primary: R2,
+}
+
+// derive(Clone) doesn't work with generic parameters that don't
+// implement clone. But, we don't need to require that C implements
+// Clone, because we're not cloning C, just the reference.
+//
+// See: https://github.com/rust-lang/rust/issues/26925
+impl<'a, P, R, R2> Clone for KeyAmalgamation<'a, P, R, R2>
+ where P: 'a + key::KeyParts,
+ R: 'a + key::KeyRole,
+ R2: Copy,
+{
+ fn clone(&self) -> Self {
+ Self {
+ ca: self.ca.clone(),
+ primary: self.primary,
+ }
+ }
+}
+
+
+/// A primary key amalgamation.
+///
+/// A specialized version of [`KeyAmalgamation`].
+///
+/// [`KeyAmalgamation`]: struct.KeyAmalgamation.html
+pub type PrimaryKeyAmalgamation<'a, P>
+ = KeyAmalgamation<'a, P, key::PrimaryRole, ()>;
+
+/// A subordinate key amalgamation.
+///
+/// A specialized version of [`KeyAmalgamation`].
+///
+/// [`KeyAmalgamation`]: struct.KeyAmalgamation.html
+pub type SubordinateKeyAmalgamation<'a, P>
+ = KeyAmalgamation<'a, P, key::SubordinateRole, ()>;
+
+/// An amalgamation whose role is not known at compile time.
+///
+/// A specialized version of [`KeyAmalgamation`].
+///
+/// Unlike a [`Key`] or a [`KeyBundle`] with an unspecified role, an
+/// `ErasedKeyAmalgamation` remembers its role; it is just not exposed
+/// to the type system. For details, see the [module-level
+/// documentation].
+///
+/// [`KeyAmalgamation`]: struct.KeyAmalgamation.html
+/// [`Key`]: ../../../packet/key/index.html
+/// [`KeyBundle`]: ../../bundle/index.html
+/// [module-level documentation]: index.html
+pub type ErasedKeyAmalgamation<'a, P>
+ = KeyAmalgamation<'a, P, key::UnspecifiedRole, bool>;
+
+
+impl<'a, P, R, R2> Deref for KeyAmalgamation<'a, P, R, R2>
+ where P: 'a + key::KeyParts,
+ R: 'a + key::KeyRole,
+{
+ type Target = ComponentAmalgamation<'a, Key<P, R>>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.ca
+ }
+}
+
+
+impl<'a, P> ValidateAmalgamation<'a, Key<P, key::PrimaryRole>>
+ for PrimaryKeyAmalgamation<'a, P>
+ where P: 'a + key::KeyParts
+{
+ type V = ValidPrimaryKeyAmalgamation<'a, P>;
+
+ fn with_policy<T>(self, policy: &'a dyn Policy, time: T)
+ -> Result<Self::V>
+ where T: Into<Option<time::SystemTime>>
+ {
+ let ka : ErasedKeyAmalgamation<P> = self.into();
+ Ok(ka.with_policy(policy, time)?
+ .try_into().expect("conversion is symmetric"))
+ }
+}
+
+impl<'a, P> ValidateAmalgamation<'a, Key<P, key::SubordinateRole>>
+ for SubordinateKeyAmalgamation<'a, P>
+ where P: 'a + key::KeyParts
+{
+ type V = ValidSubordinateKeyAmalgamation<'a, P>;
+
+ fn with_policy<T>(self, policy: &'a dyn Policy, time: T)
+ -> Result<Self::V>
+ where T: Into<Option<time::SystemTime>>
+ {
+ let ka : ErasedKeyAmalgamation<P> = self.into();
+ Ok(ka.with_policy(policy, time)?
+ .try_into().expect("conversion is symmetric"))
+ }
+}
+
+impl<'a, P> ValidateAmalgamation<'a, Key<P, key::UnspecifiedRole>>
+ for ErasedKeyAmalgamation<'a, P>
+ where P: 'a + key::KeyParts
+{
+ type V = ValidErasedKeyAmalgamation<'a, P>;
+
+ fn with_policy<T>(self, policy: &'a dyn Policy, time: T)
+ -> Result<Self::V>
+ where T: Into<Option<time::SystemTime>>
+ {
+ let time = time.into().unwrap_or_else(SystemTime::now);
+
+ // We need to make sure the certificate is okay. This means
+ // checking the primary key. But, be careful: we don't need
+ // to double check.
+ if ! self.primary() {
+ let pka = PrimaryKeyAmalgamation::new(self.cert());
+ pka.with_policy(policy, time).context("primary key")?;
+ }
+
+ let binding_signature = self.binding_signature(policy, time)?;
+ let cert = self.ca.cert();
+ let vka = ValidErasedKeyAmalgamation {
+ ka: KeyAmalgamation {
+ ca: self.ca.parts_into_public(),
+ primary: self.primary,
+ },
+ // We need some black magic to avoid infinite
+ // recursion: a ValidCert must be valid for the
+ // specified policy and reference time. A ValidCert
+ // is consider valid if the primary key is valid.
+ // ValidCert::with_policy checks that by calling this
+ // function. So, if we call ValidCert::with_policy
+ // here we'll recurse infinitely.
+ //
+ // But, hope is not lost! We know that if we get
+ // here, we've already checked that the primary key is
+ // valid (see above), or that we're in the process of
+ // evaluating the primary key's validity and we just
+ // need to check the user's policy. So, it is safe to
+ // create a ValidCert from scratch.
+ cert: ValidCert {
+ cert: cert,
+ policy: policy,
+ time: time,
+ },
+ binding_signature
+ };
+ policy.key(&vka)?;
+ Ok(ValidErasedKeyAmalgamation {
+ ka: KeyAmalgamation {
+ ca: P::convert_key_amalgamation(
+ vka.ka.ca.parts_into_unspecified()).expect("roundtrip"),
+ primary: vka.ka.primary,
+ },
+ cert: vka.cert,
+ binding_signature,
+ })
+ }
+}
+
+impl<'a, P> PrimaryKey<'a, P, key::PrimaryRole>
+ for PrimaryKeyAmalgamation<'a, P>
+ where P: 'a + key::KeyParts
+{
+ fn primary(&self) -> bool {
+ true
+ }
+}
+
+impl<'a, P> PrimaryKey<'a, P, key::SubordinateRole>
+ for SubordinateKeyAmalgamation<'a, P>
+ where P: 'a + key::KeyParts
+{
+ fn primary(&self) -> bool {
+ false
+ }
+}
+
+impl<'a, P> PrimaryKey<'a, P, key::UnspecifiedRole>
+ for ErasedKeyAmalgamation<'a, P>
+ where P: 'a + key::KeyParts
+{
+ fn primary(&self) -> bool {
+ self.primary
+ }
+}
+
+
+impl<'a, P: 'a + key::KeyParts> From<PrimaryKeyAmalgamation<'a, P>>
+ for ErasedKeyAmalgamation<'a, P>
+{
+ fn from(ka: PrimaryKeyAmalgamation<'a, P>) -> Self {
+ ErasedKeyAmalgamation {
+ ca: ka.ca.role_into_unspecified(),
+ primary: true,
+ }
+ }
+}
+
+impl<'a, P: 'a + key::KeyParts> From<SubordinateKeyAmalgamation<'a, P>>
+ for ErasedKeyAmalgamation<'a, P>
+{
+ fn from(ka: SubordinateKeyAmalgamation<'a, P>) -> Self {
+ ErasedKeyAmalgamation {
+ ca: ka.ca.role_into_unspecified(),
+ primary: false,
+ }
+ }
+}
+
+
+// We can infallibly convert part X to part Y for everything but
+// Public -> Secret and Unspecified -> Secret.
+macro_rules! impl_conversion {
+ ($s:ident, $primary:expr, $p1:path, $p2:path) => {
+ impl<'a> From<$s<'a, $p1>>
+ for ErasedKeyAmalgamation<'a, $p2>
+ {
+ fn from(ka: $s<'a, $p1>) -> Self {
+ ErasedKeyAmalgamation {
+ ca: ka.ca.into(),
+ primary: $primary,
+ }
+ }
+ }
+ }
+}
+
+impl_conversion!(PrimaryKeyAmalgamation, true,
+ key::SecretParts, key::PublicParts);
+impl_conversion!(PrimaryKeyAmalgamation, true,
+ key::SecretParts, key::UnspecifiedParts);
+impl_conversion!(PrimaryKeyAmalgamation, true,
+ key::PublicParts, key::UnspecifiedParts);
+impl_conversion!(PrimaryKeyAmalgamation, true,
+ key::UnspecifiedParts, key::PublicParts);
+
+impl_conversion!(SubordinateKeyAmalgamation, false,
+ key::SecretParts, key::PublicParts);
+impl_conversion!(SubordinateKeyAmalgamation, false,
+ key::SecretParts, key::UnspecifiedParts);
+impl_conversion!(SubordinateKeyAmalgamation, false,
+ key::PublicParts, key::UnspecifiedParts);
+impl_conversion!(SubordinateKeyAmalgamation, false,
+ key::UnspecifiedParts, key::PublicParts);
+
+
+impl<'a, P, P2> TryFrom<ErasedKeyAmalgamation<'a, P>>
+ for PrimaryKeyAmalgamation<'a, P2>
+ where P: 'a + key::KeyParts,
+ P2: 'a + key::KeyParts,
+{
+ type Error = anyhow::Error;
+
+ fn try_from(ka: ErasedKeyAmalgamation<'a, P>) -> Result<Self> {
+ if ka.primary {
+ Ok(Self {
+ ca: P2::convert_key_amalgamation(
+ ka.ca.role_into_primary().parts_into_unspecified())?,
+ primary: (),
+ })
+ } else {
+ Err(Error::InvalidArgument(
+ "can't convert a SubordinateKeyAmalgamation \
+ to a PrimaryKeyAmalgamation".into()).into())
+ }
+ }
+}
+
+impl<'a, P, P2> TryFrom<ErasedKeyAmalgamation<'a, P>>
+ for SubordinateKeyAmalgamation<'a, P2>
+ where P: 'a + key::KeyParts,
+ P2: 'a + key::KeyParts,
+{
+ type Error = anyhow::Error;
+
+ fn try_from(ka: ErasedKeyAmalgamation<'a, P>) -> Result<Self> {
+ if ka.primary {
+ Err(Error::InvalidArgument(
+ "can't convert a PrimaryKeyAmalgamation \
+ to a SubordinateKeyAmalgamation".into()).into())
+ } else {
+ Ok(Self {
+ ca: P2::convert_key_amalgamation(
+ ka.ca.role_into_subordinate().parts_into_unspecified())?,
+ primary: (),
+ })
+ }
+ }
+}
+
+impl<'a> PrimaryKeyAmalgamation<'a, key::PublicParts> {
+ pub(crate) fn new(cert: &'a Cert) -> Self {
+ PrimaryKeyAmalgamation {
+ ca: ComponentAmalgamation::new(cert, &cert.primary),
+ primary: (),
+ }
+ }
+}
+
+impl<'a, P: 'a + key::KeyParts> SubordinateKeyAmalgamation<'a, P> {
+ pub(crate) fn new(
+ cert: &'a Cert, bundle: &'a KeyBundle<P, key::SubordinateRole>)
+ -> Self
+ {
+ SubordinateKeyAmalgamation {
+ ca: ComponentAmalgamation::new(cert, bundle),
+ primary: (),
+ }
+ }
+}
+
+impl<'a, P: 'a + key::KeyParts> ErasedKeyAmalgamation<'a, P> {
+ /// Returns the key's binding signature as of the reference time,
+ /// if any.
+ ///
+ /// Note: this function is not exported. Users of this interface
+ /// should instead do: `ka.with_policy(policy,
+ /// time)?.binding_signature()`.
+ fn binding_signature<T>(&self, policy: &'a dyn Policy, time: T)
+ -> Result<&'a Signature>
+ where T: Into<Option<time::SystemTime>>
+ {
+ let time = time.into().unwrap_or_else(SystemTime::now);
+ if self.primary {
+ self.cert().primary_userid_relaxed(policy, time, false)
+ .map(|u| u.binding_signature())
+ .or_else(|e0| {
+ // Lookup of the primary user id binding failed.
+ // Look for direct key signatures.
+ self.cert().primary_key().bundle()
+ .binding_signature(policy, time)
+ .or_else(|e1| {
+ // Both lookups failed. Keep the more
+ // meaningful error.
+ if let Some(Error::NoBindingSignature(_))
+ = e1.downcast_ref()
+ {
+ Err(e0) // Return the original error.
+ } else {
+ Err(e1)
+ }
+ })
+ })
+ } else {
+ self.bundle().binding_signature(policy, time)
+ }
+ }
+}
+
+
+impl<'a, P, R, R2> KeyAmalgamation<'a, P, R, R2>
+ where P: 'a + key::KeyParts,
+ R: 'a + key::KeyRole,
+
+{
+ /// Returns the `KeyAmalgamation`'s `ComponentAmalgamation`.
+ pub fn component_amalgamation(&self)
+ -> &ComponentAmalgamation<'a, Key<P, R>> {
+ &self.ca
+ }
+
+ /// Returns the `KeyAmalgamation`'s key.
+ ///
+ /// Normally, a type implementing `KeyAmalgamation` eventually
+ /// derefs to a `Key`, howe