summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-02-13 20:22:06 +0100
committerJustus Winter <justus@sequoia-pgp.org>2023-02-14 17:44:16 +0100
commit09984cca7ca8a2b33a26853d0e8e5c0b5bcdbddc (patch)
tree5f68dfcbdc16497272913145f4f556dfebbffc4c
parent174307796ef8a4e4cc546a5334d2db0b1359af30 (diff)
very rough key6 implementation, no secrets thojustus/openpgp-next-v6-signatures-231
-rw-r--r--openpgp/src/cert/builder.rs62
-rw-r--r--openpgp/src/cert/builder/key.rs3
-rw-r--r--openpgp/src/crypto/hash.rs56
-rw-r--r--openpgp/src/crypto/key.rs77
-rw-r--r--openpgp/src/crypto/mod.rs1
-rw-r--r--openpgp/src/packet/key.rs24
-rw-r--r--openpgp/src/packet/key/conversions.rs1
-rw-r--r--openpgp/src/packet/key/v6.rs545
-rw-r--r--openpgp/src/packet/mod.rs92
-rw-r--r--openpgp/src/packet/prelude.rs1
-rw-r--r--openpgp/src/parse.rs130
-rw-r--r--openpgp/src/serialize.rs101
-rw-r--r--openpgp/src/serialize/cert.rs10
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.