diff options
Diffstat (limited to 'openpgp/src/cert/amalgamation/key.rs')
-rw-r--r-- | openpgp/src/cert/amalgamation/key.rs | 1937 |
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 |