diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2023-02-13 20:22:06 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2023-02-14 17:44:16 +0100 |
commit | 09984cca7ca8a2b33a26853d0e8e5c0b5bcdbddc (patch) | |
tree | 5f68dfcbdc16497272913145f4f556dfebbffc4c | |
parent | 174307796ef8a4e4cc546a5334d2db0b1359af30 (diff) |
very rough key6 implementation, no secrets thojustus/openpgp-next-v6-signatures-231
-rw-r--r-- | openpgp/src/cert/builder.rs | 62 | ||||
-rw-r--r-- | openpgp/src/cert/builder/key.rs | 3 | ||||
-rw-r--r-- | openpgp/src/crypto/hash.rs | 56 | ||||
-rw-r--r-- | openpgp/src/crypto/key.rs | 77 | ||||
-rw-r--r-- | openpgp/src/crypto/mod.rs | 1 | ||||
-rw-r--r-- | openpgp/src/packet/key.rs | 24 | ||||
-rw-r--r-- | openpgp/src/packet/key/conversions.rs | 1 | ||||
-rw-r--r-- | openpgp/src/packet/key/v6.rs | 545 | ||||
-rw-r--r-- | openpgp/src/packet/mod.rs | 92 | ||||
-rw-r--r-- | openpgp/src/packet/prelude.rs | 1 | ||||
-rw-r--r-- | openpgp/src/parse.rs | 130 | ||||
-rw-r--r-- | openpgp/src/serialize.rs | 101 | ||||
-rw-r--r-- | openpgp/src/serialize/cert.rs | 10 |
13 files changed, 1094 insertions, 9 deletions
diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs index 3060627c..8cc964a1 100644 --- a/openpgp/src/cert/builder.rs +++ b/openpgp/src/cert/builder.rs @@ -5,6 +5,7 @@ use crate::packet; use crate::packet::{ Key, key::Key4, + key::Key6, key::KeyRole, key::SecretKey as KeySecretKey, key::SecretParts as KeySecretParts, @@ -140,11 +141,19 @@ impl CipherSuite { Ok(()) } - fn generate_key<K, R>(self, flags: K) + fn generate_key<K, R>(self, flags: K, version: u8) -> Result<Key<KeySecretParts, R>> where R: KeyRole, K: AsRef<KeyFlags>, { + match version { + 4 => (), + 6 => (), + n => return Err(Error::InvalidArgument( + format!("Generating OpenPGP v{} keys not supported", n) + ).into()), + } + use crate::types::Curve; match self { @@ -188,7 +197,11 @@ impl CipherSuite { .into()), } }, - }.map(|key| key.into()) + }.map(|key| match version { + 4 => key.into(), + 6 => Key6::from_common(key).into(), + _ => unreachable!("checked on top of the function"), + }) } } @@ -269,6 +282,7 @@ assert_send_and_sync!(KeyBlueprint); pub struct CertBuilder<'a> { creation_time: Option<std::time::SystemTime>, ciphersuite: CipherSuite, + version: u8, primary: KeyBlueprint, subkeys: Vec<(Option<SignatureBuilder>, KeyBlueprint)>, userids: Vec<(Option<SignatureBuilder>, packet::UserID)>, @@ -321,6 +335,7 @@ impl CertBuilder<'_> { CertBuilder { creation_time: None, ciphersuite: CipherSuite::default(), + version: 4, primary: KeyBlueprint{ flags: KeyFlags::empty().set_certification(), validity: None, @@ -507,6 +522,44 @@ impl CertBuilder<'_> { self } + /// Sets the OpenPGP version to generate keys for. + /// + /// Supported are version 4 and version 6 keys. By default, we + /// generate OpenPGP v4 keys. + /// + /// # Examples + /// + /// ``` + /// use sequoia_openpgp as openpgp; + /// use openpgp::cert::prelude::*; + /// use openpgp::types::PublicKeyAlgorithm; + /// + /// # fn main() -> openpgp::Result<()> { + /// let (key, _) = + /// CertBuilder::general_purpose(None, Some("alice@example.org")) + /// .generate()?; + /// assert_eq!(key.primary_key().version(), 4); + /// + /// let (key, _) = + /// CertBuilder::general_purpose(None, Some("alice@example.org")) + /// .set_version(6)? + /// .generate()?; + /// assert_eq!(key.primary_key().version(), 6); + /// # Ok(()) + /// # } + /// ``` + pub fn set_version(mut self, version: u8) -> Result<Self> { + match version { + 4 => (), + 6 => (), + n => return Err(Error::InvalidArgument( + format!("Generating OpenPGP v{} keys not supported", n) + ).into()), + } + self.version = version; + Ok(self) + } + /// Adds a User ID. /// /// Adds a User ID to the certificate. The first User ID that is @@ -1441,7 +1494,7 @@ impl CertBuilder<'_> { let flags = &blueprint.flags; let mut subkey = blueprint.ciphersuite .unwrap_or(self.ciphersuite) - .generate_key(flags)?; + .generate_key(flags, self.version)?; subkey.set_creation_time(creation_time)?; let sig = template.unwrap_or_else( @@ -1492,7 +1545,8 @@ impl CertBuilder<'_> { { let mut key = self.primary.ciphersuite .unwrap_or(self.ciphersuite) - .generate_key(KeyFlags::empty().set_certification())?; + .generate_key(KeyFlags::empty().set_certification(), + self.version)?; key.set_creation_time(creation_time)?; let sig = SignatureBuilder::new(SignatureType::DirectKey); let sig = Self::signature_common(sig, creation_time)?; diff --git a/openpgp/src/cert/builder/key.rs b/openpgp/src/cert/builder/key.rs index 50ba3229..75908a21 100644 --- a/openpgp/src/cert/builder/key.rs +++ b/openpgp/src/cert/builder/key.rs @@ -143,8 +143,9 @@ impl KeyBuilder { /// NTP is widely used, empirically it seems that some virtual /// machines have laggy clocks. pub fn subkey(self, vc: ValidCert) -> Result<SubkeyBuilder<'_>> { + let version = vc.primary_key().version(); let mut key: Key<key::SecretParts, key::SubordinateRole> - = self.cipher_suite.generate_key(&self.flags)?; + = self.cipher_suite.generate_key(&self.flags, version)?; let ct = self.creation_time.unwrap_or_else(|| { crate::now() - Duration::new(SIG_BACKDATE_BY, 0) }); diff --git a/openpgp/src/crypto/hash.rs b/openpgp/src/crypto/hash.rs index dfac7892..61568cc7 100644 --- a/openpgp/src/crypto/hash.rs +++ b/openpgp/src/crypto/hash.rs @@ -38,7 +38,7 @@ use crate::packet::Key; use crate::packet::UserID; use crate::packet::UserAttribute; use crate::packet::key; -use crate::packet::key::Key4; +use crate::packet::key::{Key4, Key6}; use crate::packet::Signature; use crate::packet::signature::{self, Signature3, Signature4, Signature6}; use crate::Result; @@ -369,6 +369,18 @@ impl Hash for UserAttribute { } } +impl<P, R> Hash for Key<P, R> + where P: key::KeyParts, + R: key::KeyRole, +{ + fn hash(&self, hash: &mut dyn Digest) { + match self { + Key::V4(k) => k.hash(hash), + Key::V6(k) => k.hash(hash), + } + } +} + impl<P, R> Hash for Key4<P, R> where P: key::KeyParts, R: key::KeyRole, @@ -408,6 +420,48 @@ impl<P, R> Hash for Key4<P, R> } } +impl<P, R> Hash for Key6<P, R> + where P: key::KeyParts, + R: key::KeyRole, +{ + fn hash(&self, hash: &mut dyn Digest) { + use crate::serialize::MarshalInto; + + // We hash 15 bytes plus the MPIs. But, the len doesn't + // include the tag (1 byte) or the length (4 bytes). + let len = (15 - 5) + self.mpis().serialized_len() as u32; + + let mut header: Vec<u8> = Vec::with_capacity(9); + + // Tag. + header.push(0x9b); + + // Length (4 bytes, big endian). + header.extend_from_slice(&len.to_be_bytes()); + + // Version. + header.push(6); + + // Creation time. + let creation_time: u32 = + Timestamp::try_from(self.creation_time()) + .unwrap_or_else(|_| Timestamp::from(0)) + .into(); + header.extend_from_slice(&creation_time.to_be_bytes()); + + // Algorithm. + header.push(self.pk_algo().into()); + + // Length of all MPIs. + header.extend_from_slice( + &(self.mpis().serialized_len() as u32).to_be_bytes()); + hash.update(&header[..]); + + // MPIs. + self.mpis().hash(hash); + } +} + impl Hash for Signature { fn hash(&self, hash: &mut dyn Digest) { match self { diff --git a/openpgp/src/crypto/key.rs b/openpgp/src/crypto/key.rs new file mode 100644 index 00000000..fd3f9eb0 --- /dev/null +++ b/openpgp/src/crypto/key.rs @@ -0,0 +1,77 @@ +//! Common secret key related operations. + +use std::time::SystemTime; + +use crate::{ + Result, + packet::key::{self, Key4, Key6, SecretParts}, + types::{ + Curve, + HashAlgorithm, + SymmetricAlgorithm, + }, +}; + +impl<R> Key6<SecretParts, R> + where R: key::KeyRole, +{ + + /// 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<H, S, T>(private_key: &[u8], + hash: H, sym: S, ctime: T) + -> Result<Self> where H: Into<Option<HashAlgorithm>>, + S: Into<Option<SymmetricAlgorithm>>, + T: Into<Option<SystemTime>> + { + Key4::import_secret_cv25519(private_key, hash, sym, ctime) + .map(Key6::from_common) + } + + /// 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<T>(private_key: &[u8], ctime: T) + -> Result<Self> where T: Into<Option<SystemTime>> + { + Key4::import_secret_ed25519(private_key, ctime) + .map(Key6::from_common) + } + + /// 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. + #[allow(clippy::many_single_char_names)] + pub fn import_secret_rsa<T>(d: &[u8], p: &[u8], q: &[u8], ctime: T) + -> Result<Self> where T: Into<Option<SystemTime>> + { + Key4::import_secret_rsa(d, p, q, ctime) + .map(Key6::from_common) + } + + /// Generates a new RSA key with a public modulos of size `bits`. + pub fn generate_rsa(bits: usize) -> Result<Self> { + Key4::generate_rsa(bits) + .map(Key6::from_common) + } + + /// 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. Likewise + /// `for_signing == false` and `curve == Ed25519` will produce an error. + pub fn generate_ecc(for_signing: bool, curve: Curve) -> Result<Self> { + Key4::generate_ecc(for_signing, curve) + .map(Key6::from_common) + } +} diff --git a/openpgp/src/crypto/mod.rs b/openpgp/src/crypto/mod.rs index 6dc2002d..75adbe09 100644 --- a/openpgp/src/crypto/mod.rs +++ b/openpgp/src/crypto/mod.rs @@ -34,6 +34,7 @@ pub use self::asymmetric::{Signer, Decryptor, KeyPair}; mod backend; pub mod ecdh; pub mod hash; +mod key; pub mod mem; pub mod mpi; mod s2k; diff --git a/openpgp/src/packet/key.rs b/openpgp/src/packet/key.rs index 83d563f4..ae06596d 100644 --- a/openpgp/src/packet/key.rs +++ b/openpgp/src/packet/key.rs @@ -108,6 +108,8 @@ use crate::KeyHandle; use crate::policy::HashAlgoSecurity; mod conversions; +mod v6; +pub use v6::Key6; /// A marker trait that captures whether a `Key` definitely contains /// secret key material. @@ -1644,9 +1646,14 @@ impl<P, R> Arbitrary for super::Key<P, R> where P: KeyParts, P: Clone, R: KeyRole, R: Clone, Key4<P, R>: Arbitrary, + Key6<P, R>: Arbitrary, { fn arbitrary(g: &mut Gen) -> Self { - Key4::arbitrary(g).into() + if <bool>::arbitrary(g) { + Key4::arbitrary(g).into() + } else { + Key6::arbitrary(g).into() + } } } @@ -2297,4 +2304,19 @@ FwPoSAbbsLkNS/iNN2MDGAVYvezYn2QZ mutate_eq_discriminates_key(key, i) } } + + #[test] + fn v6_key_fingerprint() -> Result<()> { + let p = Packet::from_bytes("-----BEGIN PGP ARMORED FILE----- + +xjcGY4d/4xYAAAAtCSsGAQQB2kcPAQEHQPlNp7tI1gph5WdwamWH0DMZmbudiRoI +JC6thFQ9+JWj +=SgmS +-----END PGP ARMORED FILE-----")?; + let k: &Key<PublicParts, PrimaryRole> = p.downcast_ref().unwrap(); + assert_eq!(k.fingerprint().to_string(), + "4EADF309C6BC874AE04702451548F93F\ + 96FA7A01D0A33B5AF7D4E379E0F9F8EE".to_string()); + Ok(()) + } } diff --git a/openpgp/src/packet/key/conversions.rs b/openpgp/src/packet/key/conversions.rs index 150e1e9a..2d5d8490 100644 --- a/openpgp/src/packet/key/conversions.rs +++ b/openpgp/src/packet/key/conversions.rs @@ -416,6 +416,7 @@ macro_rules! create_conversions { create_conversions!(Key<>); create_conversions!(Key4<>); +create_conversions!(Key6<>); create_conversions!(KeyBundle<>); // A hack, since the type has to be an ident, which means that we diff --git a/openpgp/src/packet/key/v6.rs b/openpgp/src/packet/key/v6.rs new file mode 100644 index 00000000..f0711e50 --- /dev/null +++ b/openpgp/src/packet/key/v6.rs @@ -0,0 +1,545 @@ +//! OpenPGP v6 key packet. + +use std::fmt; +use std::cmp::Ordering; +use std::hash::Hasher; +use std::time; + +#[cfg(test)] +use quickcheck::{Arbitrary, Gen}; + +use crate::crypto::{mpi, hash::{Hash, Digest}}; +use crate::packet::key::{ + KeyParts, + KeyRole, + PublicParts, + SecretParts, + UnspecifiedParts, +}; +use crate::packet::prelude::*; +use crate::PublicKeyAlgorithm; +use crate::SymmetricAlgorithm; +use crate::HashAlgorithm; +use crate::types::Timestamp; +use crate::Result; +use crate::crypto::Password; +use crate::KeyID; +use crate::Fingerprint; +use crate::KeyHandle; +use crate::policy::HashAlgoSecurity; + +/// Holds a public key, public subkey, private key or private subkey +/// packet. +/// +/// Use [`Key6::generate_rsa`] or [`Key6::generate_ecc`] to create a +/// new key. +/// +/// Existing key material can be turned into an OpenPGP key using +/// [`Key6::new`], [`Key6::with_secret`], [`Key6::import_public_cv25519`], +/// [`Key6::import_public_ed25519`], [`Key6::import_public_rsa`], +/// [`Key6::import_secret_cv25519`], [`Key6::import_secret_ed25519`], +/// and [`Key6::import_secret_rsa`]. +/// +/// Whether you create a new key or import existing key material, you +/// still need to create a binding signature, and, for signing keys, a +/// back signature before integrating the key into a certificate. +/// +/// Normally, you won't directly use `Key6`, but [`Key`], which is a +/// relatively thin wrapper around `Key6`. +/// +/// See [Section 5.5 of RFC 4880] and [the documentation for `Key`] +/// for more details. +/// +/// [Section 5.5 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.5 +/// [the documentation for `Key`]: super::Key +/// [`Key`]: super::Key +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct Key6<P: KeyParts, R: KeyRole> { + pub(crate) common: Key4<P, R>, +} + +impl<P, R> fmt::Debug for Key6<P, R> +where P: KeyParts, + R: KeyRole, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Key6") + .field("fingerprint", &self.fingerprint()) + .field("creation_time", &self.creation_time()) + .field("pk_algo", &self.pk_algo()) + .field("mpis", &self.mpis()) + .field("secret", &self.optional_secret()) + .finish() + } +} + +impl<P, R> fmt::Display for Key6<P, R> +where P: KeyParts, + R: KeyRole, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.fingerprint()) + } +} + +impl<P, R> Key6<P, R> +where P: KeyParts, + R: KeyRole, +{ + /// The security requirements of the hash algorithm for + /// self-signatures. + /// + /// A cryptographic hash algorithm usually has [three security + /// properties]: pre-image resistance, second pre-image + /// resistance, and collision resistance. If an attacker can + /// influence the signed data, then the hash algorithm needs to + /// have both second pre-image resistance, and collision + /// resistance. If not, second pre-image resistance is + /// sufficient. + /// + /// [three security properties]: https://en.wikipedia.org/wiki/Cryptographic_hash_function#Properties + /// + /// In general, an attacker may be able to influence third-party + /// signatures. But direct key signatures, and binding signatures + /// are only over data fully determined by signer. And, an + /// attacker's control over self signatures over User IDs is + /// limited due to their structure. + /// + /// These observations can be used to extend the life of a hash + /// algorithm after its collision resistance has been partially + /// compromised, but not completely broken. For more details, + /// please refer to the documentation for [HashAlgoSecurity]. + /// + /// [HashAlgoSecurity]: crate::policy::HashAlgoSecurity + pub fn hash_algo_security(&self) -> HashAlgoSecurity { + HashAlgoSecurity::SecondPreImageResistance + } + + /// Compares the public bits of two keys. + /// + /// This returns `Ordering::Equal` if the public MPIs, creation + /// time, and algorithm of the two `Key6`s match. This does not + /// consider the packets' encodings, packets' tags or their secret + /// key material. + pub fn public_cmp<PB, RB>(&self, b: &Key6<PB, RB>) -> Ordering + where PB: KeyParts, + RB: KeyRole, + { + self.mpis().cmp(b.mpis()) + .then_with(|| self.creation_time().cmp(&b.creation_time())) + .then_with(|| self.pk_algo().cmp(&b.pk_algo())) + } + + /// Tests whether two keys are equal modulo their secret key + /// material. + /// + /// This returns true if the public MPIs, creation time and + /// algorithm of the two `Key6`s match. This does not consider + /// the packets' encodings, packets' tags or their secret key + /// material. + pub fn public_eq<PB, RB>(&self, b: &Key6<PB, RB>) -> bool + where PB: KeyParts, + RB: KeyRole, + { + self.public_cmp(b) == Ordering::Equal + } + + /// Hashes everything but any secret key material into state. + /// + /// This is an alternate implementation of [`Hash`], which never + /// hashes the secret key material. + /// + /// [`Hash`]: std::hash::Hash + pub fn public_hash<H>(&self, state: &mut H) + where H: Hasher + { + self.common.public_hash(state); + } +} + +impl<P, R> Key6<P, R> +where + P: KeyParts, + R: KeyRole, +{ + /// Creates a v6 key from a v4 key. Used internally in + /// constructors. + pub(crate) fn from_common(common: Key4<P, R>) -> Self { + Key6 { common } + } + + /// Creates an OpenPGP public key from the specified key material. + /// + /// This is an internal version for parse.rs that avoids going + /// through SystemTime. + pub(crate) fn make<T>(creation_time: T, + pk_algo: PublicKeyAlgorithm, + mpis: mpi::PublicKey, + secret: Option<SecretKeyMaterial>) + -> Result<Self> + where + T: Into<Timestamp>, + { + Ok(Key6 { + common: Key4::make(creation_time, pk_algo, mpis, secret)?, + }) + } +} + +impl<R> Key6<key::PublicParts, R> +where R: KeyRole, +{ + /// Creates an OpenPGP public key from the specified key material. + pub fn new<T>(creation_time: T, pk_algo: PublicKeyAlgorithm, + mpis: mpi::PublicKey) + -> Result<Self> + where T: Into<time::SystemTime> + { + Ok(Key6 { + common: Key4::new(creation_time, pk_algo, mpis)?, + }) + } + + /// Creates an OpenPGP public key packet from existing X25519 key + /// material. + /// + /// 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 its creation date set to + /// `ctime` or the current time if `None` is given. + pub fn import_public_cv25519<H, S, T>(public_key: &[u8], + hash: H, sym: S, ctime: T) + -> Result<Self> where H: Into<Option<HashAlgorithm>>, + S: Into<Option<SymmetricAlgorithm>>, + T: Into<Option<time::SystemTime>> + { + Ok(Key6 { + common: Key4::import_public_cv25519(public_key, hash, sym, ctime)?, + }) + } + + /// Creates an OpenPGP public key packet from existing Ed25519 key + /// material. + /// + /// 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 its creation date set to + /// `ctime` or the current time if `None` is given. + pub fn import_public_ed25519<T>(public_key: &[u8], ctime: T) -> Result<Self> + where T: Into<Option<time::SystemTime>> + { + Ok(Key6 { + common: Key4::import_public_ed25519(public_key, ctime)?, + }) + } + + /// Creates an OpenPGP public key packet from existing RSA key + /// material. + /// + /// The RSA key will use the public exponent `e` and the modulo + /// `n`. The key will have its creation date set to `ctime` or the + /// current time if `None` is given. + pub fn import_public_rsa<T>(e: &[u8], n: &[u8], ctime: T) + -> Result<Self> where T: Into<Option<time::SystemTime>> + { + Ok(Key6 { + common: Key4::import_public_rsa(e, n, ctime)?, + }) + } +} + +impl<R> Key6<SecretParts, R> +where R: KeyRole, +{ + /// Creates an OpenPGP key packet from the specified secret key + /// material. + pub fn with_secret<T>(creation_time: T, pk_algo: PublicKeyAlgorithm, + mpis: mpi::PublicKey, + secret: SecretKeyMaterial) + -> Result<Self> + where T: Into<time::SystemTime> + { + Ok(Key6 { + common: Key4::with_secret(creation_time, pk_algo, mpis, secret)?, + }) + } +} +impl<P, R> Key6<P, R> +where P: KeyParts, + R: KeyRole, +{ + /// Gets the `Key`'s creation time. + pub fn creation_time(&self) -> time::SystemTime { + self.common.creation_time() + } + + /// Gets the `Key`'s creation time without converting it to a + /// system time. + /// + /// This conversion may truncate the time to signed 32-bit time_t. + pub(crate) fn creation_time_raw(&self) -> Timestamp { + self.common.creation_time_raw() + } + + /// Sets the `Key`'s creation time. + /// + /// `timestamp` is converted to OpenPGP's internal format, + /// [`Timestamp`]: a 32-bit quantity containing the number of + /// seconds since the Unix epoch. + /// + /// `timestamp` is silently rounded to match the internal + /// resolution. An error is returned if `timestamp` is out of + /// range. + /// + /// [`Timestamp`]: crate::types::Timestamp + pub fn set_creation_time<T>(&mut self, timestamp: T) + -> Result<time::SystemTime> + where T: Into<time::SystemTime> + { + self.common.set_creation_time(timestamp) + } + + /// Gets the public key algorithm. + pub fn pk_algo(&self) -> PublicKeyAlgorithm { + self.common.pk_algo() + } + + /// Sets the public key algorithm. + /// + /// Returns the old public key algorithm. + pub fn set_pk_algo(&mut self, pk_algo: PublicKeyAlgorithm) + -> PublicKeyAlgorithm + { + self.common.set_pk_algo(pk_algo) + } + + /// Returns a reference to the `Key`'s MPIs. + pub fn mpis(&self) -> &mpi::PublicKey { + self.common.mpis() + } + + /// Returns a mutable reference to the `Key`'s MPIs. + pub fn mpis_mut(&mut self) -> &mut mpi::PublicKey { + self.common.mpis_mut() + } + + /// Sets the `Key`'s MPIs. + /// + /// This function returns the old MPIs, if any. + pub fn set_mpis(&mut self, mpis: mpi::PublicKey) -> mpi::PublicKey { + self.common.set_mpis(mpis) + } + + /// Returns whether the `Key` contains secret key material. + pub fn has_secret(&self) -> bool { + self.common.has_secret() + } + + /// Returns whether the `Key` contains unencrypted secret key + /// material. + /// + /// This returns false if the `Key` doesn't contain any secret key + /// material. + pub fn has_unencrypted_secret(&self) -> bool { + self.common.has_unencrypted_secret() + } + + /// Returns `Key`'s secret key material, if any. |