From 6b18fb9388a5e9f19ae3637ea65cfc9b257056cc Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Wed, 1 Apr 2020 14:51:28 +0200 Subject: openpgp: Rename. --- openpgp/src/packet/key.rs | 2006 +++++++++++++++++++++++++++++++++++++++++ openpgp/src/packet/key/mod.rs | 2006 ----------------------------------------- 2 files changed, 2006 insertions(+), 2006 deletions(-) create mode 100644 openpgp/src/packet/key.rs delete mode 100644 openpgp/src/packet/key/mod.rs (limited to 'openpgp') diff --git a/openpgp/src/packet/key.rs b/openpgp/src/packet/key.rs new file mode 100644 index 00000000..722bead4 --- /dev/null +++ b/openpgp/src/packet/key.rs @@ -0,0 +1,2006 @@ +//! Public key, public subkey, private key and private subkey packets. +//! Key variants. +//! +//! There are four variants of OpenPGP keys: public keys, public +//! subkeys, secret keys, and secret subkeys. These are based on +//! the cross product of two attributes: whether the key contains +//! any secret key material, and the key's role. +//! +//! The underlying representation of these four variants is +//! identical (even a public key and a secret key are the same: +//! the public key variant just contains 0 bits of secret key +//! material), and many (but not all) operations can be done on +//! all four variants. +//! +//! We separate these variants into two types: parts (public or +//! secret) and roles (primary or secondary). We also add +//! unspecified variants, because sometimes we want a slice of +//! keys, and we don't care about the key's role. For instance, +//! when iterating over all of the keys in a Cert, we want the +//! primary and the subkeys. These can't be put in the same slice +//! without first wrapping them, which is awkward. +//! +//! For the most part, the user doesn't need to worry about the +//! markers. Occasionally, it is necessary to change a key's markers. +//! For these cases, it is possible to just use the `From` trait to +//! get the require markers. But, it is also possible to explicitly +//! set markers. Compare: +//! +//! ```rust +//! # extern crate sequoia_openpgp as openpgp; +//! # use openpgp::Result; +//! # use openpgp::parse::{Parse, PacketParserResult, PacketParser}; +//! # use openpgp::cert::prelude::*; +//! use openpgp::packet::{Key, key}; +//! +//! # fn main() { f().unwrap(); } +//! # fn f() -> Result<()> +//! # { +//! # let (cert, _) = CertBuilder::new() +//! # .set_cipher_suite(CipherSuite::Cv25519) +//! # .generate()?; +//! // Get a handle to the Cert's primary key that allows using the +//! // secret key material. +//! use std::convert::TryInto; +//! let sk: &Key = cert.primary_key().key().try_into()?; +//! +//! // Make the conversion explicit. +//! let sk = cert.primary_key().key().mark_parts_secret_ref()?; +//! # Ok(()) +//! # } +//! ``` + +use std::fmt; +use std::cmp::Ordering; +use std::convert::{TryFrom, TryInto}; +use std::time; + +use crate::Error; +use crate::cert::prelude::*; +use crate::crypto::{self, mem::{self, Protected}, mpis, hash::Hash}; +use crate::packet; +use crate::packet::prelude::*; +use crate::PublicKeyAlgorithm; +use crate::SymmetricAlgorithm; +use crate::HashAlgorithm; +use crate::types::{Curve, Timestamp}; +use crate::crypto::S2K; +use crate::Result; +use crate::crypto::Password; +use crate::KeyID; +use crate::Fingerprint; +use crate::KeyHandle; + +/// A marker trait that indicates whether a `Key` only contains +/// public key material or *may* also contains secret key +/// material. +pub trait KeyParts: fmt::Debug { + /// Converts a key with unspecified parts into this kind of key. + fn convert_key(key: Key) + -> Result> + where Self: Sized; + + /// Converts a reference to a key with unspecified parts into this + /// kind of key reference. + fn convert_key_ref(key: &Key) + -> Result<&Key> + where Self: Sized; + + /// Converts a key bundle with unspecified parts into this kind of + /// key bundle. + fn convert_bundle(bundle: KeyBundle) + -> Result> + where Self: Sized; + + /// Converts a reference to a key bundle with unspecified parts + /// into this kind of key bundle reference. + fn convert_bundle_ref(bundle: &KeyBundle) + -> Result<&KeyBundle> + where Self: Sized; + + /// Converts a key amalgamation with unspecified parts into this + /// kind of key amalgamation. + fn convert_key_amalgamation<'a, R: KeyRole>( + ka: ComponentAmalgamation<'a, Key>) + -> Result>> + where Self: Sized; + + /// Converts a reference to a key amalgamation with unspecified + /// parts into this kind of key amalgamation reference. + fn convert_key_amalgamation_ref<'a, R: KeyRole>( + ka: &'a ComponentAmalgamation<'a, Key>) + -> Result<&'a ComponentAmalgamation<'a, Key>> + where Self: Sized; +} + +/// A marker trait that indicates whether a `Key` is a primary key or +/// subordinate key (i.e., a subkey). +pub trait KeyRole: fmt::Debug { + /// Converts a key with unspecified role into this kind of key. + fn convert_key(key: Key) + -> Key + where Self: Sized; + + /// Converts a reference to a key with unspecified role into this + /// kind of key reference. + fn convert_key_ref(key: &Key) + -> &Key + where Self: Sized; + + /// Converts a key bundle with unspecified role into this kind of + /// key bundle. + fn convert_bundle(bundle: KeyBundle) + -> KeyBundle + where Self: Sized; + + /// Converts a reference to a key bundle with unspecified role + /// into this kind of key bundle reference. + fn convert_bundle_ref(bundle: &KeyBundle) + -> &KeyBundle + where Self: Sized; +} + +/// Indicates that a `Key` should be treated like a public key. +/// +/// Note: this doesn't indicate whether the data structure contains +/// secret key material; it indicates whether any secret key material +/// should be ignored. For instance, when exporting a key with the +/// `PublicParts` marker, secret key material will *not* be exported. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct PublicParts; +impl KeyParts for PublicParts { + fn convert_key(key: Key) + -> Result> { + Ok(key.into()) + } + + fn convert_key_ref(key: &Key) + -> Result<&Key> { + Ok(key.into()) + } + + fn convert_bundle(bundle: KeyBundle) + -> Result> { + Ok(bundle.into()) + } + + fn convert_bundle_ref(bundle: &KeyBundle) + -> Result<&KeyBundle> { + Ok(bundle.into()) + } + + fn convert_key_amalgamation<'a, R: KeyRole>( + ka: ComponentAmalgamation<'a, Key>) + -> Result>> { + Ok(ka.into()) + } + + /// Converts a reference to a key amalgamation with unspecified + /// parts into this kind of key amalgamation reference. + fn convert_key_amalgamation_ref<'a, R: KeyRole>( + ka: &'a ComponentAmalgamation<'a, Key>) + -> Result<&'a ComponentAmalgamation<'a, Key>> { + Ok(ka.into()) + } +} + +/// Indicates that a `Key` should be treated like a secret key. +/// +/// Note: this doesn't indicate whether the data structure contains +/// secret key material; it indicates whether any secret key material +/// should be used. For instance, when exporting a key with the +/// `SecretParts` marker, secret key material will be exported. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct SecretParts; +impl KeyParts for SecretParts { + fn convert_key(key: Key) + -> Result>{ + key.try_into() + } + + fn convert_key_ref(key: &Key) + -> Result<&Key> { + key.try_into() + } + + fn convert_bundle(bundle: KeyBundle) + -> Result> { + bundle.try_into() + } + + fn convert_bundle_ref(bundle: &KeyBundle) + -> Result<&KeyBundle> { + bundle.try_into() + } + + fn convert_key_amalgamation<'a, R: KeyRole>( + ka: ComponentAmalgamation<'a, Key>) + -> Result>> { + ka.try_into() + } + + /// Converts a reference to a key amalgamation with unspecified + /// parts into this kind of key amalgamation reference. + fn convert_key_amalgamation_ref<'a, R: KeyRole>( + ka: &'a ComponentAmalgamation<'a, Key>) + -> Result<&'a ComponentAmalgamation<'a, Key>> { + ka.try_into() + } +} + +/// Indicates that a `Key`'s parts are unspecified. +/// +/// Neither public key-specific nor secret key-specific operations are +/// allowed on such keys. +/// +/// For instance, it is not possible to export a key with the +/// `UnspecifiedParts` marker, because it is unclear how to treat any +/// secret key material. To export such a key, you need to use a +/// different `KeyParts` marker. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct UnspecifiedParts; +impl KeyParts for UnspecifiedParts { + fn convert_key(key: Key) + -> Result> { + Ok(key) + } + + fn convert_key_ref(key: &Key) + -> Result<&Key> { + Ok(key) + } + + fn convert_bundle(bundle: KeyBundle) + -> Result> { + Ok(bundle) + } + + fn convert_bundle_ref(bundle: &KeyBundle) + -> Result<&KeyBundle> { + Ok(bundle) + } + + fn convert_key_amalgamation<'a, R: KeyRole>( + ka: ComponentAmalgamation<'a, Key>) + -> Result>> { + Ok(ka.into()) + } + + /// Converts a reference to a key amalgamation with unspecified + /// parts into this kind of key amalgamation reference. + fn convert_key_amalgamation_ref<'a, R: KeyRole>( + ka: &'a ComponentAmalgamation<'a, Key>) + -> Result<&'a ComponentAmalgamation<'a, Key>> { + Ok(ka.into()) + } +} + +/// Indicates that a `Key` should treated like a primary key. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct PrimaryRole; +impl KeyRole for PrimaryRole { + fn convert_key(key: Key) + -> Key { + key.into() + } + + fn convert_key_ref(key: &Key) + -> &Key { + key.into() + } + + fn convert_bundle(bundle: KeyBundle) + -> KeyBundle { + bundle.into() + } + + fn convert_bundle_ref(bundle: &KeyBundle) + -> &KeyBundle { + bundle.into() + } +} + +/// Indicates that a `Key` should treated like a subkey key. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct SubordinateRole; +impl KeyRole for SubordinateRole { + fn convert_key(key: Key) + -> Key { + key.into() + } + + fn convert_key_ref(key: &Key) + -> &Key { + key.into() + } + + fn convert_bundle(bundle: KeyBundle) + -> KeyBundle { + bundle.into() + } + + fn convert_bundle_ref(bundle: &KeyBundle) + -> &KeyBundle { + bundle.into() + } +} + +/// Indicates that a `Key`'s role is unknown. +/// +/// Neither primary key-specific nor subkey-specific operations +/// are allowed. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct UnspecifiedRole; +impl KeyRole for UnspecifiedRole { + fn convert_key(key: Key) + -> Key { + key + } + + fn convert_key_ref(key: &Key) + -> &Key { + key + } + + fn convert_bundle(bundle: KeyBundle) + -> KeyBundle { + bundle + } + + fn convert_bundle_ref(bundle: &KeyBundle) + -> &KeyBundle { + bundle + } +} + +/// A Public Key. +pub(crate) type PublicKey = Key; +/// A Public Subkey. +pub(crate) type PublicSubkey = Key; +/// A Secret Key. +pub(crate) type SecretKey = Key; +/// A Secret Subkey. +pub(crate) type SecretSubkey = Key; + +/// A key with public parts, and an unspecified role +/// (`UnspecifiedRole`). +#[allow(dead_code)] +pub(crate) type UnspecifiedPublic = Key; +/// A key with secret parts, and an unspecified role +/// (`UnspecifiedRole`). +pub(crate) type UnspecifiedSecret = Key; + +/// A primary key with unspecified parts (`UnspecifiedParts`). +#[allow(dead_code)] +pub(crate) type UnspecifiedPrimary = Key; +/// A subkey key with unspecified parts (`UnspecifiedParts`). +#[allow(dead_code)] +pub(crate) type UnspecifiedSecondary = Key; + +/// A key whose parts and role are unspecified +/// (`UnspecifiedParts`, `UnspecifiedRole`). +#[allow(dead_code)] +pub(crate) type UnspecifiedKey = Key; + +macro_rules! convert { + ( $x:ident ) => { + // XXX: This is ugly, but how can we do better? + unsafe { std::mem::transmute($x) } + } +} + +macro_rules! convert_ref { + ( $x:ident ) => { + // XXX: This is ugly, but how can we do better? + unsafe { std::mem::transmute($x) } + } +} + +// Make it possible to go from an arbitrary Key to an +// arbitrary Key (or &Key to &Key) in a +// single .into(). +// +// To allow the programmer to make the intent clearer, also +// provide explicit conversion function. + +// In principle, this is as easy as the following: +// +// impl From> for Key +// where P: KeyParts, P2: KeyParts, R: KeyRole, R2: KeyRole +// { +// fn from(p: Key) -> Self { +// unimplemented!() +// } +// } +// +// But that results in: +// +// error[E0119]: conflicting implementations of trait `std::convert::From>` for type `packet::Key<_, _>`: +// = note: conflicting implementation in crate `core`: +// - impl std::convert::From for T; +// +// Unfortunately, it's not enough to make one type variable +// concrete, as the following errors demonstrate: +// +// error[E0119]: conflicting implementations of trait `std::convert::From>` for type `packet::Key`: +// ... +// = note: conflicting implementation in crate `core`: +// - impl std::convert::From for T; +// +// impl From> for Key +// where P: KeyParts, R: KeyRole, R2: KeyRole +// { +// fn from(p: Key) -> Self { +// unimplemented!() +// } +// } +// +// error[E0119]: conflicting implementations of trait `std::convert::From>` for type `packet::Key`: +// --> openpgp/src/packet/key.rs:186:5 +// ... +// = note: conflicting implementation in crate `core`: +// - impl std::convert::From for T; +// impl From> for Key +// where P2: KeyParts, R: KeyRole, R2: KeyRole +// { +// fn from(p: Key) -> Self { +// unimplemented!() +// } +// } +// +// To solve this, we need at least one generic variable to be +// concrete on both sides of the `From`. + +macro_rules! create_part_conversions { + ( $Key:ident<$( $l:lifetime ),*; $( $g:ident ),*>) => { + create_part_conversions!($Key<$($l),*; $($g),*> where ); + }; + ( $Key:ident<$( $l:lifetime ),*; $( $g:ident ),*> where $( $w:ident: $c:path ),* ) => { + // Convert between two KeyParts for a constant KeyRole. + // Unfortunately, we can't let the KeyRole vary as otherwise we + // get conflicting types when we do the same to convert between + // two KeyRoles for a constant KeyParts. :( + macro_rules! p { + ( <$from_parts:ty> -> <$to_parts:ty> ) => { + impl<$($l, )* $($g, )* > From<$Key<$($l, )* $from_parts, $($g, )* >> for $Key<$($l, )* $to_parts, $($g, )* > + where $($w: $c ),* + { + fn from(p: $Key<$($l, )* $from_parts, $($g, )* >) -> Self { + convert!(p) + } + } + + impl<$($l, )* $($g, )* > From<&$($l)* $Key<$($l, )* $from_parts, $($g, )* >> for &$($l)* $Key<$($l, )* $to_parts, $($g, )* > + where $($w: $c ),* + { + fn from(p: &$($l)* $Key<$($l, )* $from_parts, $($g, )* >) -> Self { + convert_ref!(p) + } + } + } + } + + // Likewise, but using TryFrom. + macro_rules! p_try { + ( <$from_parts:ty> -> <$to_parts:ty>) => { + impl<$($l, )* $($g, )* > TryFrom<$Key<$($l, )* $from_parts, $($g, )* >> for $Key<$($l, )* $to_parts, $($g, )* > + where $($w: $c ),* + { + type Error = anyhow::Error; + fn try_from(p: $Key<$($l, )* $from_parts, $($g, )* >) -> Result { + p.mark_parts_secret() + } + } + + impl<$($l, )* $($g, )* > TryFrom<&$($l)* $Key<$($l, )* $from_parts, $($g, )* >> for &$($l)* $Key<$($l, )* $to_parts, $($g, )* > + where $($w: $c ),* + { + type Error = anyhow::Error; + fn try_from(p: &$($l)* $Key<$($l, )* $from_parts, $($g, )* >) -> Result { + if p.has_secret() { + Ok(convert_ref!(p)) + } else { + Err(Error::InvalidArgument("No secret key".into()) + .into()) + } + } + } + } + } + + + p_try!( -> ); + p!( -> ); + + p!( -> ); + p!( -> ); + + p!( -> ); + p_try!( -> ); + + + impl<$($l, )* P, $($g, )*> $Key<$($l, )* P, $($g, )*> where P: KeyParts, $($w: $c ),* + { + /// Changes the key's parts tag to `PublicParts`. + pub fn mark_parts_public(self) -> $Key<$($l, )* PublicParts, $($g, )*> { + // Ideally, we'd use self.into() to do the actually + // conversion. But, because P is not concrete, we get the + // following error: + // + // error[E0277]: the trait bound `packet::Key: std::convert::From>` is not satisfied + // --> openpgp/src/packet/key.rs:401:18 + // | + // 401 | self.into() + // | ^^^^ the trait `std::convert::From>` is not implemented for `packet::Key` + // | + // = help: consider adding a `where packet::Key: std::convert::From>` bound + // = note: required because of the requirements on the impl of `std::convert::Into>` for `packet::Key` + // + // But we can't implement implement `From>` for + // `Key`, because that conflicts with a + // standard conversion! (See the comment for the `p` + // macro above.) + // + // Adding the trait bound is annoying, because then we'd + // have to add it everywhere that we use into. + convert!(self) + } + + /// Changes the key's parts tag to `PublicParts`. + pub fn mark_parts_public_ref(&$($l)* self) -> &$($l)* $Key<$($l, )* PublicParts, $($g, )*> { + convert_ref!(self) + } + + /// Changes the key's parts tag to `SecretParts`. + pub fn mark_parts_secret(self) -> Result<$Key<$($l, )* SecretParts, $($g, )*>> { + if self.has_secret() { + Ok(convert!(self)) + } else { + Err(Error::InvalidArgument("No secret key".into()).into()) + } + } + + /// Changes the key's parts tag to `SecretParts`. + pub fn mark_parts_secret_ref(&$($l)* self) -> Result<&$($l)* $Key<$($l, )* SecretParts, $($g, )*>> + { + if self.has_secret() { + Ok(convert_ref!(self)) + } else { + Err(Error::InvalidArgument("No secret key".into()).into()) + } + } + + /// Changes the key's parts tag to `UnspecifiedParts`. + pub fn mark_parts_unspecified(self) -> $Key<$($l, )* UnspecifiedParts, $($g, )*> { + convert!(self) + } + + /// Changes the key's parts tag to `UnspecifiedParts`. + pub fn mark_parts_unspecified_ref(&$($l)* self) -> &$Key<$($l, )* UnspecifiedParts, $($g, )*> { + convert_ref!(self) + } + } + } +} + +macro_rules! create_role_conversions { + ( $Key:ident<$( $l:lifetime ),*> ) => { + // Convert between two KeyRoles for a constant KeyParts. See + // the comment for the p macro above. + macro_rules! r { + ( <$from_role:ty> -> <$to_role:ty>) => { + impl<$($l, )* P> From<$Key<$($l, )* P, $from_role>> for $Key<$($l, )* P, $to_role> + where P: KeyParts + { + fn from(p: $Key<$($l, )* P, $from_role>) -> Self { + convert!(p) + } + } + + impl<$($l, )* P> From<&$($l)* $Key<$($l, )* P, $from_role>> for &$($l)* $Key<$($l, )* P, $to_role> + where P: KeyParts + { + fn from(p: &$($l)* $Key<$($l, )* P, $from_role>) -> Self { + convert_ref!(p) + } + } + } + } + + r!( -> ); + r!( -> ); + + r!( -> ); + r!( -> ); + + r!( -> ); + r!( -> ); + } +} + +macro_rules! create_conversions { + ( $Key:ident<$( $l:lifetime ),*> ) => { + create_part_conversions!($Key<$($l ),* ; R> where R: KeyRole); + create_role_conversions!($Key<$($l ),* >); + + // We now handle converting both the part and the role at the same + // time. + + macro_rules! f { + ( <$from_parts:ty, $from_role:ty> -> <$to_parts:ty, $to_role:ty> ) => { + impl<$($l ),*> From<$Key<$($l, )* $from_parts, $from_role>> for $Key<$($l, )* $to_parts, $to_role> + { + fn from(p: $Key<$($l, )* $from_parts, $from_role>) -> Self { + convert!(p) + } + } + + impl<$($l ),*> From<&$($l)* $Key<$($l, )* $from_parts, $from_role>> for &$($l)* $Key<$($l, )* $to_parts, $to_role> + { + fn from(p: &$($l)* $Key<$from_parts, $from_role>) -> Self { + convert_ref!(p) + } + } + } + } + + // The calls that are comment out are the calls for the + // combinations where either the KeyParts or the KeyRole does not + // change. + + //f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + + //f!( -> ); + //f!( -> ); + //f!( -> ); + f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + f!( -> ); + + //f!( -> ); + //f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + + f!( -> ); + //f!( -> ); + f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + f!( -> ); + //f!( -> ); + f!( -> ); + + f!( -> ); + f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + + f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + + f!( -> ); + f!( -> ); + //f!( -> ); + f!( -> ); + f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + //f!( -> ); + + + impl<$($l, )* P, R> $Key<$($l, )* P, R> where P: KeyParts, R: KeyRole + { + /// Changes the key's role tag to `PrimaryRole`. + pub fn mark_role_primary(self) -> $Key<$($l, )* P, PrimaryRole> { + convert!(self) + } + + /// Changes the key's role tag to `PrimaryRole`. + pub fn mark_role_primary_ref(&$($l)* self) -> &$($l)* $Key<$($l, )* P, PrimaryRole> { + convert_ref!(self) + } + + /// Changes the key's role tag to `SubordinateRole`. + pub fn mark_role_subordinate(self) -> $Key<$($l, )* P, SubordinateRole> + { + convert!(self) + } + + /// Changes the key's role tag to `SubordinateRole`. + pub fn mark_role_subordinate_ref(&$($l)* self) -> &$($l)* $Key<$($l, )* P, SubordinateRole> + { + convert_ref!(self) + } + + /// Changes the key's role tag to `UnspecifiedRole`. + pub fn mark_role_unspecified(self) -> $Key<$($l, )* P, UnspecifiedRole> + { + convert!(self) + } + + /// Changes the key's role tag to `UnspecifiedRole`. + pub fn mark_role_unspecified_ref(&$($l)* self) -> &$($l)* $Key<$($l, )* P, UnspecifiedRole> + { + convert_ref!(self) + } + } + } +} + +impl KeyBundle +{ + fn has_secret(&self) -> bool { + self.key().secret.is_some() + } +} + +create_conversions!(Key<>); +create_conversions!(Key4<>); +create_conversions!(KeyBundle<>); + +// A hack, since the type has to be an ident, which means that we +// can't use <>. +type KeyComponentAmalgamation<'a, P, R> = ComponentAmalgamation<'a, Key>; +create_conversions!(KeyComponentAmalgamation<'a>); + +create_part_conversions!(PrimaryKeyAmalgamation<'a;>); +create_part_conversions!(SubordinateKeyAmalgamation<'a;>); +create_part_conversions!(ErasedKeyAmalgamation<'a;>); +create_part_conversions!(ValidPrimaryKeyAmalgamation<'a;>); +create_part_conversions!(ValidSubordinateKeyAmalgamation<'a;>); +create_part_conversions!(ValidErasedKeyAmalgamation<'a;>); + +/// Holds a public key, public subkey, private key or private subkey packet. +/// +/// See [Section 5.5 of RFC 4880] for details. +/// +/// [Section 5.5 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.5 +#[derive(Clone)] +pub struct Key4 + where P: KeyParts, R: KeyRole +{ + /// CTB packet header fields. + pub(crate) common: packet::Common, + /// When the key was created. + creation_time: Timestamp, + /// Public key algorithm of this signature. + pk_algo: PublicKeyAlgorithm, + /// Public key MPIs. + mpis: mpis::PublicKey, + /// Optional secret part of the key. + secret: Option, + + p: std::marker::PhantomData

, + r: std::marker::PhantomData, +} + +impl PartialEq for Key4 { + fn eq(&self, other: &Key4) -> bool { + self.creation_time == other.creation_time + && self.pk_algo == other.pk_algo + && self.mpis == other.mpis + && self.secret == other.secret + } +} + +impl Eq for Key4 {} + +impl std::hash::Hash for Key4 { + fn hash(&self, state: &mut H) { + std::hash::Hash::hash(&self.creation_time, state); + std::hash::Hash::hash(&self.pk_algo, state); + std::hash::Hash::hash(&self.mpis, state); + std::hash::Hash::hash(&self.secret, state); + } +} + +impl fmt::Debug for Key4 + where P: key::KeyParts, + R: key::KeyRole, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Key4") + .field("fingerprint", &self.fingerprint()) + .field("creation_time", &self.creation_time) + .field("pk_algo", &self.pk_algo) + .field("mpis", &self.mpis) + .field("secret", &self.secret) + .finish() + } +} + +impl fmt::Display for Key4 + where P: key::KeyParts, + R: key::KeyRole, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.fingerprint()) + } +} + +impl Key4 + where P: key::KeyParts, + R: key::KeyRole, +{ + /// Compares the public bits of two keys. + /// + /// This returns Ordering::Equal if the public MPIs, + /// creation time and algorithm of the two `Key4`s match. This + /// does not consider the packet's encoding, packet's tag or the + /// secret key material. + pub fn public_cmp(&self, b: &Key4) -> Ordering + where PB: key::KeyParts, + RB: key::KeyRole, + { + match self.mpis.cmp(&b.mpis) { + Ordering::Equal => (), + o => return o, + } + + match self.creation_time.cmp(&b.creation_time) { + Ordering::Equal => (), + o => return o, + } + + self.pk_algo.cmp(&b.pk_algo) + } + + /// This method tests for self and other values to be equal modulo + /// the secret bits. + /// + /// This returns true if the public MPIs, creation time and + /// algorithm of the two `Key4`s match. This does not consider + /// the packet's encoding, packet's tag or the secret key + /// material. + pub fn public_eq(&self, b: &Key4) -> bool + where PB: key::KeyParts, + RB: key::KeyRole, + { + self.public_cmp(b) == Ordering::Equal + } +} + +impl Key4 + where R: key::KeyRole, +{ + /// Creates a new OpenPGP key packet. + pub fn new(creation_time: T, pk_algo: PublicKeyAlgorithm, + mpis: mpis::PublicKey) + -> Result + where T: Into + { + Ok(Key4 { + common: Default::default(), + creation_time: creation_time.into().try_into()?, + pk_algo, + mpis, + secret: None, + p: std::marker::PhantomData, + r: std::marker::PhantomData, + }) + } + + /// Creates a new OpenPGP public key packet for an existing X25519 key. + /// + /// The ECDH key will use hash algorithm `hash` and symmetric + /// algorithm `sym`. If one or both are `None` secure defaults + /// will be used. The key will have it's creation date set to + /// `ctime` or the current time if `None` is given. + pub fn import_public_cv25519(public_key: &[u8], + hash: H, sym: S, ctime: T) + -> Result where H: Into>, + S: Into>, + T: Into> + { + let mut point = Vec::from(public_key); + point.insert(0, 0x40); + + Self::new( + ctime.into() + .unwrap_or_else(|| time::SystemTime::now()), + PublicKeyAlgorithm::ECDH, + mpis::PublicKey::ECDH { + curve: Curve::Cv25519, + hash: hash.into().unwrap_or(HashAlgorithm::SHA512), + sym: sym.into().unwrap_or(SymmetricAlgorithm::AES256), + q: mpis::MPI::new(&point), + }) + } + + /// Creates a new OpenPGP public key packet for an existing Ed25519 key. + /// + /// The ECDH key will use hash algorithm `hash` and symmetric + /// algorithm `sym`. If one or both are `None` secure defaults + /// will be used. The key will have it's creation date set to + /// `ctime` or the current time if `None` is given. + pub fn import_public_ed25519(public_key: &[u8], ctime: T) -> Result + where T: Into> + { + let mut point = Vec::from(public_key); + point.insert(0, 0x40); + + Self::new( + ctime.into() + .unwrap_or_else(|| time::SystemTime::now()), + PublicKeyAlgorithm::EdDSA, + mpis::PublicKey::EdDSA { + curve: Curve::Ed25519, + q: mpis::MPI::new(&point), + }) + } + + /// Creates a new OpenPGP public key packet for an existing RSA key. + /// + /// The RSA key will use public exponent `e` and modulo `n`. The key will + /// have it's creation date set to `ctime` or the current time if `None` + /// is given. + pub fn import_public_rsa(e: &[u8], n: &[u8], ctime: T) + -> Result where T: Into> + { + Self::new( + ctime.into() + .unwrap_or_else(|| time::SystemTime::now()), + PublicKeyAlgorithm::RSAEncryptSign, + mpis::PublicKey::RSA { + e: mpis::MPI::new(e), + n: mpis::MPI::new(n), + }) + } +} + +impl Key4 + where R: key::KeyRole, +{ + /// Creates a new OpenPGP key packet with secrets. + pub fn with_secret(creation_time: T, pk_algo: PublicKeyAlgorithm, + mpis: mpis::PublicKey, + secret: SecretKeyMaterial) + -> Result + where T: Into + { + Ok(Key4 { + common: Default::default(), + creation_time: creation_time.into().try_into()?, + pk_algo, + mpis, + secret: Some(secret), + p: std::marker::PhantomData, + r: std::marker::PhantomData, + }) + } + + /// Creates a new OpenPGP secret key packet for an existing X25519 key. + /// + /// The ECDH key will use hash algorithm `hash` and symmetric + /// algorithm `sym`. If one or both are `None` secure defaults + /// will be used. The key will have it's creation date set to + /// `ctime` or the current time if `None` is given. + pub fn import_secret_cv25519(private_key: &[u8], + hash: H, sym: S, ctime: T) + -> Result where H: Into>, + S: Into>, + T: Into> + { + use nettle::curve25519::{self, CURVE25519_SIZE}; + + let mut public_key = [0x40u8; CURVE25519_SIZE + 1]; + curve25519::mul_g(&mut public_key[1..], private_key).unwrap(); + + let mut private_key = Vec::from(private_key); + private_key.reverse(); + + Self::with_secret( + ctime.into() + .unwrap_or_else(|| time::SystemTime::now()), + PublicKeyAlgorithm::ECDH, + mpis::PublicKey::ECDH { + curve: Curve::Cv25519, + hash: hash.into().unwrap_or(HashAlgorithm::SHA512), + sym: sym.into().unwrap_or(SymmetricAlgorithm::AES256), + q: mpis::MPI::new(&public_key), + }, + mpis::SecretKeyMaterial::ECDH { + scalar: private_key.into(), + }.into()) + } + + /// Creates a new OpenPGP secret key packet for an existing Ed25519 key. + /// + /// The ECDH key will use hash algorithm `hash` and symmetric + /// algorithm `sym`. If one or both are `None` secure defaults + /// will be used. The key will have it's creation date set to + /// `ctime` or the current time if `None` is given. + pub fn import_secret_ed25519(private_key: &[u8], ctime: T) + -> Result where T: Into> + { + use nettle::ed25519::{self, ED25519_KEY_SIZE}; + + let mut public_key = [0x40u8; ED25519_KEY_SIZE + 1]; + ed25519::public_key(&mut public_key[1..], private_key).unwrap(); + + Self::with_secret( + ctime.into() + .unwrap_or_else(|| time::SystemTime::now()), + PublicKeyAlgorithm::EdDSA, + mpis::PublicKey::EdDSA { + curve: Curve::Ed25519, + q: mpis::MPI::new(&public_key), + }, + mpis::SecretKeyMaterial::EdDSA { + scalar: mpis::MPI::new(private_key).into(), + }.into()) + } + + /// Creates a new OpenPGP public key packet for an existing RSA key. + /// + /// The RSA key will use public exponent `e` and modulo `n`. The key will + /// have it's creation date set to `ctime` or the current time if `None` + /// is given. + pub fn import_secret_rsa(d: &[u8], p: &[u8], q: &[u8], ctime: T) + -> Result where T: Into> + { + use nettle::rsa; + + let sec = rsa::PrivateKey::new(d, p, q, None)?; + let key = sec.public_key()?; + let (a, b, c) = sec.as_rfc4880(); + + Self::with_secret( + ctime.into() + .unwrap_or_else(|| time::SystemTime::now()), + PublicKeyAlgorithm::RSAEncryptSign, + mpis::PublicKey::RSA { + e: mpis::MPI::new(&key.e()[..]), + n: mpis::MPI::new(&key.n()[..]), + }, + mpis::SecretKeyMaterial::RSA { + d: mpis::MPI::new(d).into(), + p: mpis::MPI::new(&a[..]).into(), + q: mpis::MPI::new(&b[..]).into(), + u: mpis::MPI::new(&c[..]).into(), + }.into()) + } + + /// Generates a new RSA key with a public modulos of size `bits`. + pub fn generate_rsa(bits: usize) -> Result { + use nettle::{rsa, random::Yarrow}; + use crate::crypto::mpis::{MPI, PublicKey}; + + let mut rng = Yarrow::default(); + let (public, private) = rsa::generate_keypair(&mut rng, bits as u32)?; + let (p, q, u) = private.as_rfc4880(); + let public_mpis = PublicKey::RSA { + e: MPI::new(&*public.e()).into(), + n: MPI::new(&*public.n()).into(), + }; + let private_mpis = mpis::SecretKeyMaterial::RSA { + d: MPI::new(&*private.d()).into(), + p: MPI::new(&*p).into(), + q: MPI::new(&*q).into(), + u: MPI::new(&*u).into(), + }; + + Self::with_secret( + time::SystemTime::now(), + PublicKeyAlgorithm::RSAEncryptSign, + public_mpis, + private_mpis.into()) + } + + /// Generates a new ECC key over `curve`. + /// + /// If `for_signing` is false a ECDH key, if it's true either a + /// EdDSA or ECDSA key is generated. Giving `for_signing == true` + /// and `curve == Cv25519` will produce an error. Similar for + /// `for_signing == false` and `curve == Ed25519`. + /// signing/encryption + pub fn generate_ecc(for_signing: bool, curve: Curve) -> Result { + use nettle::{ + random::Yarrow, + ed25519, ed25519::ED25519_KEY_SIZE, + curve25519, curve25519::CURVE25519_SIZE, + ecc, ecdh, ecdsa, + }; + use crate::crypto::mpis::{MPI, PublicKey}; + use crate::PublicKeyAlgorithm::*; + + let mut rng = Yarrow::default(); + + let (mpis, secret, pk_algo) = match (curve.clone(), for_signing) { + (Curve::Ed25519, true) => { + let mut public = [0u8; ED25519_KEY_SIZE + 1]; + let private: Protected = + ed25519::private_key(&mut rng).into(); + + public[0] = 0x40; + ed25519::public_key(&mut public[1..], &private)?; + + let public_mpis = PublicKey::EdDSA { + curve: Curve::Ed25519, + q: MPI::new(&public), + }; + let private_mpis = mpis::SecretKeyMaterial::EdDSA { + scalar: private.into(), + }; + let sec = private_mpis.into(); + + (public_mpis, sec, EdDSA) + } + + (Curve::Cv25519, false) => { + let mut public = [0u8; CURVE25519_SIZE + 1]; + let mut private: Protected = + curve25519::private_key(&mut rng).into(); + + public[0] = 0x40; + + curve25519::mul_g(&mut public[1..], &private)?; + + // Reverse the scalar. See + // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html. + private.reverse(); + + let public_mpis = PublicKey::ECDH { + curve: Curve::Cv25519, + q: MPI::new(&public), + hash: HashAlgorithm::SHA256, + sym: SymmetricAlgorithm::AES256, + }; + let private_mpis = mpis::SecretKeyMaterial::ECDH { + scalar: private.into(), + }; + let sec = private_mpis.into(); + + (public_mpis, sec, ECDH) + } + + (Curve::NistP256, true) | (Curve::NistP384, true) + | (Curve::NistP521, true) => { + let (public, private, field_sz) = match curve { + Curve::NistP256 => { + let (pu, sec) = + ecdsa::generate_keypair::(&mut rng)?; + (pu, sec, 256) + } + Curve::NistP384 => { + let (pu, sec) = + ecdsa::generate_keypair::(&mut rng)?; + (pu, sec, 384) + } + Curve::NistP521 => { + let (pu, sec) = + ecdsa::generate_keypair::(&mut rng)?; + (pu, sec, 521) + } + _ => unreachable!(), + }; + let (pub_x, pub_y) = public.as_bytes(); + let public_mpis = mpis::PublicKey::ECDSA{ + curve, + q: MPI::new_weierstrass(&pub_x, &pub_y, field_sz), + }; + let private_mpis = mpis::SecretKeyMaterial::ECDSA{ + scalar: MPI::new(&private.as_bytes()).into(), + }; + let sec = private_mpis.into(); + + (public_mpis, sec, ECDSA) + } + + (Curve::NistP256, false) | (Curve::NistP384, false) + | (Curve::NistP521, false) => { + let (private, hash, field_sz) = match curve { + Curve::NistP256 => { + let pv = + ecc::Scalar::new_random::(&mut rng); + + (pv, HashAlgorithm::SHA256, 256) + } + Curve::NistP384 => { + let pv = + ecc::Scalar::new_random::(&mut rng); + + (pv, HashAlgorithm::SHA384, 384) + } + Curve::NistP521 => { + let pv = + ecc::Scalar::new_random::(&mut rng); + + (pv, HashAlgorithm::SHA512, 521) + } + _ => unreachable!(), + }; + let public = ecdh::point_mul_g(&private); + let (pub_x, pub_y) = public.as_bytes(); + let public_mpis = mpis::PublicKey::ECDH{ + curve, + q: MPI::new_weierstrass(&pub_x, &pub_y, field_sz), + hash, + sym: SymmetricAlgorithm::AES256, + }; + let private_mpis = mpis::SecretKeyMaterial::ECDH{ + scalar: MPI::new(&private.as_bytes()).into(), + }; + let sec = private_mpis.into(); + + (public_mpis, sec, ECDH) + } + + (cv, _) => { + return Err(Error::UnsupportedEllipticCurve(cv).into()); + } + }; + + Self::with_secret( + time::SystemTime::now(), + pk_algo, + mpis, + secret) + } +} + +impl Key4 + where P: key::KeyParts, + R: key::KeyRole, +{ + /// Gets the key packet's creation time field. + pub fn creation_time(&self) -> time::SystemTime { + self.creation_time.into() + } + + /// Sets the key packet's creation time field. + pub fn set_creation_time(&mut self, timestamp: T) + -> Result + where T: Into + { + Ok(std::mem::replace(&mut self.creation_time, + timestamp.into().try_into()?) + .into()) + } + + /// Gets the public key algorithm. + pub fn pk_algo(&self) -> PublicKeyAlgorithm { + self.pk_algo + } + + /// Sets the public key algorithm. + pub fn set_pk_algo(&mut self, pk_algo: PublicKeyAlgorithm) -> PublicKeyAlgorithm { + ::std::mem::replace(&mut self.pk_algo, pk_algo) + } + + /// Gets the key packet's MPIs. + pub fn mpis(&self) -> &mpis::PublicKey { + &self.mpis + } + + /// Gets a mutable reference to the key packet's MPIs. + pub fn mpis_mut(&mut self) -> &mut mpis::PublicKey { + &mut self.mpis + } + + /// Sets the key packet's MPIs. + pub fn set_mpis(&mut self, mpis: mpis::PublicKey) -> mpis::PublicKey { + ::std::mem::replace(&mut self.mpis, mpis) + } + + /// Returns whether the key contains secret key material. + pub fn has_secret(&self) -> bool { + self.secret.is_some() + } + + /// Returns whether the key contains unencrypted secret key + /// material. + pub fn has_unencrypted_secret(&self) -> bool + { + if let Some(secret) = &self.secret { + if let SecretKeyMaterial::Unencrypted { .. } = secret { + true + } else { + false + } + } else { + false + } + } + + /// Gets the key packet's `SecretKeyMaterial`, if any. + pub fn optional_secret(&self) -> Option<&SecretKeyMaterial> { + self.secret.as_ref() + } + + /// Computes and returns the key's fingerprint as per Section 12.2 + /// of RFC 4880 as returns it as a KeyHandle. + pub fn key_handle(&self) -> KeyHandle { + self.fingerprint().into() + } + + /// Computes and returns the key's fingerprint as per Section 12.2 + /// of RFC 4880. + pub fn fingerprint(&self) -> Fingerprint { + let mut h = HashAlgorithm::SHA1.context().unwrap(); + + self.hash(&mut h); + + let mut digest = vec![0u8; h.digest_size()]; + h.digest(&mut digest); + Fingerprint::from_bytes(digest.as_slice()) + } + + /// Computes and returns the key's key ID as per Section 12.2 of + /// RFC 4880. + pub fn keyid(&self) -> KeyID { + self.fingerprint().into() + } +} + +macro_rules! impl_common_secret_functions { + ($t: ident) => { + /// Secret key handling. + impl Key4<$t, R> + where R: key::KeyRole, + { + /// Takes the key packet's `SecretKeyMaterial`, if any. + pub fn take_secret(mut self) + -> (Key4, Option) + { + let old = std::mem::replace(&mut self.secret, None); + (self.mark_parts_public(), old) + } + + /// Adds `SecretKeyMaterial` to the packet, returning the old if + /// any. + pub fn add_secret(mut self, secret: SecretKeyMaterial) + -> (Key4, Option) + { + let old = std::mem::replace(&mut self.secret, Some(secret)); + (self.mark_parts_secret().expect("secret just set"), old) + } + } + } +} +impl_common_secret_functions!(PublicParts); +impl_common_secret_functions!(UnspecifiedParts); + +/// Secret key handling. +impl Key4 + where R: key::KeyRole, +{ + /// Gets the key packet's `SecretKeyMaterial`. + pub fn secret(&self) -> &SecretKeyMaterial { + self.secret.as_ref().expect("has secret") + } + + /// Gets a mutable reference to the key packet's + /// `SecretKeyMaterial`. + pub fn secret_mut(&mut self) -> &mut SecretKeyMaterial { + self.secret.as_mut().expect("has secret") + } + + /// Takes the key packet's `SecretKeyMaterial`. + pub fn take_secret(mut self) + -> (Key4, SecretKeyMaterial) + { + let old = std::mem::replace(&mut self.secret, None); + (self.mark_parts_public(), + old.expect("Key has a secret key material")) + } + + /// Adds `SecretKeyMaterial` to the packet, returning the old if + /// any. + pub fn add_secret(mut self, secret: SecretKeyMaterial) + -> (Key4, SecretKeyMaterial) + { + let old = std::mem::replace(&mut self.secret, Some(secret)); + (self.mark_parts_secret().expect("secret just set"), + old.expect("Key has a secret key material")) + } +} + +impl From> for super::Key + where P: key::KeyParts, + R: key::KeyRole, +{ + fn from(p: Key4) -> Self { + super::Key::V4(p) + } +} + +/// Holds the secret potion of a OpenPGP secret key or secret subkey packet. +/// +/// This type allows postponing the decryption of the secret key until we need to use it. +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub enum SecretKeyMaterial { + /// Unencrypted secret key. Can be used as-is. + Unencrypted(Unencrypted), + /// The secret key is encrypted with a password. + Encrypted(Encrypted), +} + +impl From for SecretKeyMaterial { + fn from(mpis: mpis::SecretKeyMaterial) -> Self { + SecretKeyMaterial::Unencrypted(mpis.into()) + } +} + +impl From for SecretKeyMaterial { + fn from(key: Unencrypted) -> Self { + SecretKeyMaterial::Unencrypted(key) + } +} + +impl From for SecretKeyMaterial { + fn from(key: Encrypted) -> Self { + SecretKeyMaterial::Encrypted(key) + } +} + +impl SecretKeyMaterial { + /// Decrypts this secret key using `password`. + /// + /// The `SecretKeyMaterial` type does not know what kind of key it is, so + /// `pk_algo` is needed to parse the correct number of MPIs. + pub fn decrypt_in_place(&mut self, pk_algo: PublicKeyAlgorithm, + password: &Password) + -> Result<()> { + let new = match self { + SecretKeyMaterial::Encrypted(ref e) => + Some(e.decrypt(pk_algo, password)?.into()), + SecretKeyMaterial::Unencrypted(_) => None, + }; + + if let Some(v) = new { + *self = v; + } + + Ok(()) + } + + /// Encrypts this secret key using `password`. + pub fn encrypt_in_place(&mut self, password: &Password) -> Result<()> { + let new = match self { + SecretKeyMaterial::Unencrypted(ref u) => + Some(u.encrypt(password)?.into()), + SecretKeyMaterial::Encrypted(_) => None, + }; + + if let Some(v) = new { + *self = v; + } + + Ok(()) + } + + /// Returns true if this secret key is encrypted. + pub fn is_encrypted(&self) -> bool { + match self { + SecretKeyMaterial::Encrypted(_) => true, + SecretKeyMaterial::Unencrypted(_) => false, + } + } +} + +/// Unencrypted secret key. Can be used as-is. +/// +/// The secret key is encrypted in memory and only decrypted on +/// demand. See [`crypto::mem::Encrypted`] for details. +/// +/// [`crypto::mem::Encrypted`]: ../../crypto/mem/struct.Encrypted.html +// Note: PartialEq, Eq, and Hash on mem::Encrypted does the right +// thing. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Unencrypted { + /// MPIs of the secret key. + mpis: mem::Encrypted, +} + +impl From for Unencrypted { + fn from(mpis: mpis::SecretKeyMaterial) -> Self { + use crate::serialize::Marshal; + let mut plaintext = Vec::new(); + // We need to store the type. + plaintext.push( + mpis.algo().unwrap_or(PublicKeyAlgorithm::Unknown(0)).into()); + mpis.serialize(&mut plaintext) + .expect("MPI serialization to vec failed"); + Unencrypted { mpis: mem::Encrypted::new(plaintext.into()), } + } +} + +impl Unencrypted { + /// Maps the given function over the secret. + pub fn map(&self, mut fun: F) -> T + where F: FnMut(&mpis::SecretKeyMaterial) -> T + { + self.mpis.map(|plaintext| { + let algo: PublicKeyAlgorithm = plaintext[0].into(); + let mpis = mpis::SecretKeyMaterial::parse(algo, &plaintext[1..]) + .expect("Decrypted secret key is malformed"); + fun(&mpis) + }) + } + + /// Encrypts this secret key using `password`. + pub fn encrypt(&self, password: &Password) + -> Result { + use std::io::Write; + use crate::crypto::symmetric::Encryptor; + + let s2k = S2K::default(); + let algo = SymmetricAlgorithm::AES256; + let key = s2k.derive_key(password, algo.key_size()?)?; + + // Ciphertext is preceded by a random block. + let mut trash = vec![0u8; algo.block_size()?]; + crypto::random(&mut trash); + + let mut esk = Vec::new(); + { + let mut encryptor = Encryptor::new(algo, &key, &mut esk)?; + encryptor.write_all(&trash)?; + self.map(|mpis| mpis.serialize_chksumd(&mut encryptor))?; + } + + Ok(Encrypted { s2k, algo, ciphertext: esk.into_boxed_slice() }) + } +} + +/// The secret key is encrypted with a password. +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct Encrypted { + /// Key derivation mechanism to use. + s2k: S2K, + /// Symmetric algorithm used for encryption the secret key. + algo: SymmetricAlgorithm, + /// Encrypted MPIs prefixed with the IV. + ciphertext: Box<[u8]>, +} + +impl Encrypted { + /// Creates a new encrypted key object. + pub fn new(s2k: S2K, algo: SymmetricAlgorithm, ciphertext: Box<[u8]>) + -> Self { + Encrypted { s2k, algo, ciphertext } + } + + /// Returns the key derivation mechanism. + pub fn s2k(&self) -> &S2K { + &self.s2k + } + + /// Returns the symmetric algorithm used for encryption the secret + /// key. + pub fn algo(&self) -> SymmetricAlgorithm { + self.algo + } + + /// Returns the key derivation mechanism. + pub fn ciphertext(&self) -> &[u8] { + &self.ciphertext + } + + /// Decrypts this secret key using `password`. + /// + /// The `Encrypted` key does not know what kind of key it is, so + /// `pk_algo` is needed to parse the correct number of MPIs. + pub fn decrypt(&self, pk_algo: PublicKeyAlgorithm, password: &Password) + -> Result { + use std::io::{Cursor, Read}; + use crate::crypto::symmetric::Decryptor; + + let key = self.s2k.derive_key(password, self.algo.key_size()?)?; + let cur = Cursor::new(&self.ciphertext); + let mut dec = Decryptor::new(self.algo, &key, cur)?; + + // Consume the first block. + let mut trash = vec![0u8; self.algo.block_size()?]; + dec.read_exact(&mut trash)?; + + mpis::SecretKeyMaterial::parse_chksumd(pk_algo, &mut dec).map(|m| m.into()) +