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>2024-03-13 10:59:48 +0100
commit710996262e5dc3e39649e210eb58c5a93e5ab2d9 (patch)
treeb567d39745b85cc91837abd18e7882049a842268
parenta3c0e1737d921de757c957bc9d831d85d446c4a1 (diff)
openpgp: Implement v6 key packet support.
todo: - encrypt/decrypt secrets
-rw-r--r--openpgp/src/cert/builder.rs123
-rw-r--r--openpgp/src/cert/builder/key.rs4
-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.rs61
-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.rs240
-rw-r--r--openpgp/src/serialize.rs122
-rw-r--r--openpgp/src/serialize/cert.rs10
13 files changed, 1318 insertions, 15 deletions
diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs
index 0d9f77b1..51b3ea4b 100644
--- a/openpgp/src/cert/builder.rs
+++ b/openpgp/src/cert/builder.rs
@@ -5,7 +5,8 @@ use crate::packet;
use crate::packet::{
Key,
key::Key4,
- key::KeyRole,
+ key::Key6,
+ key::UnspecifiedRole,
key::SecretKey as KeySecretKey,
key::SecretParts as KeySecretParts,
};
@@ -140,10 +141,22 @@ impl CipherSuite {
Ok(())
}
- fn generate_key<K, R>(self, flags: K)
- -> Result<Key<KeySecretParts, R>>
- where R: KeyRole,
- K: AsRef<KeyFlags>,
+ fn generate_key<K>(self, flags: K, version: u8)
+ -> Result<Key<KeySecretParts, UnspecifiedRole>>
+ where K: AsRef<KeyFlags>,
+ {
+ match version {
+ 4 => Ok(self.generate_v4_key(flags)?.into()),
+ 6 => Ok(self.generate_v6_key(flags)?.into()),
+ n => Err(Error::InvalidArgument(
+ format!("Generating OpenPGP v{} keys not supported", n)
+ ).into()),
+ }
+ }
+
+ fn generate_v4_key<K>(self, flags: K)
+ -> Result<Key4<KeySecretParts, UnspecifiedRole>>
+ where K: AsRef<KeyFlags>,
{
use crate::types::Curve;
@@ -188,7 +201,58 @@ impl CipherSuite {
.into()),
}
},
- }.map(|key| key.into())
+ }
+ }
+
+ fn generate_v6_key<K>(self, flags: K)
+ -> Result<Key6<KeySecretParts, UnspecifiedRole>>
+ where K: AsRef<KeyFlags>,
+ {
+ use crate::types::Curve;
+
+ let flags = flags.as_ref();
+ let sign = flags.for_certification() || flags.for_signing()
+ || flags.for_authentication();
+ let encrypt = flags.for_transport_encryption()
+ || flags.for_storage_encryption();
+
+ match self {
+ CipherSuite::RSA2k =>
+ Key4::generate_rsa(2048),
+ CipherSuite::RSA3k =>
+ Key4::generate_rsa(3072),
+ CipherSuite::RSA4k =>
+ Key4::generate_rsa(4096),
+ CipherSuite::Cv25519 |
+ CipherSuite::P256 | CipherSuite::P384 | CipherSuite::P521 => {
+ let curve = match self {
+ CipherSuite::Cv25519 if sign => Curve::Ed25519,
+ CipherSuite::Cv25519 if encrypt => Curve::Cv25519,
+ CipherSuite::Cv25519 => {
+ return Err(Error::InvalidOperation(
+ "No key flags set".into())
+ .into());
+ }
+ CipherSuite::P256 => Curve::NistP256,
+ CipherSuite::P384 => Curve::NistP384,
+ CipherSuite::P521 => Curve::NistP521,
+ _ => unreachable!(),
+ };
+
+ match (sign, encrypt) {
+ (true, false) => Key4::generate_ecc(true, curve),
+ (false, true) => Key4::generate_ecc(false, curve),
+ (true, true) =>
+ Err(Error::InvalidOperation(
+ "Can't use key for encryption and signing".into())
+ .into()),
+ (false, false) =>
+ Err(Error::InvalidOperation(
+ "No key flags set".into())
+ .into()),
+ }
+ },
+ }.map(Key6::from_common)
}
}
@@ -269,6 +333,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)>,
@@ -322,6 +387,7 @@ impl CertBuilder<'_> {
CertBuilder {
creation_time: None,
ciphersuite: CipherSuite::default(),
+ version: 4,
primary: KeyBlueprint{
flags: KeyFlags::empty().set_certification(),
validity: None,
@@ -552,6 +618,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
@@ -1495,7 +1599,8 @@ impl CertBuilder<'_> {
let flags = &blueprint.flags;
let mut subkey = blueprint.ciphersuite
.unwrap_or(self.ciphersuite)
- .generate_key(flags)?;
+ .generate_key(flags, self.version)?
+ .role_into_subordinate();
subkey.set_creation_time(creation_time)?;
let sig = template.unwrap_or_else(
@@ -1552,7 +1657,9 @@ 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)?
+ .role_into_primary();
key.set_creation_time(creation_time)?;
let sig = SignatureBuilder::new(SignatureType::DirectKey);
let sig = Self::signature_common(
diff --git a/openpgp/src/cert/builder/key.rs b/openpgp/src/cert/builder/key.rs
index 4d211750..1619c82a 100644
--- a/openpgp/src/cert/builder/key.rs
+++ b/openpgp/src/cert/builder/key.rs
@@ -143,8 +143,10 @@ 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)?
+ .role_into_subordinate();
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 7bd9a9e1..f1ef2098 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};
use crate::Result;
@@ -384,6 +384,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,
@@ -423,6 +435,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 1c136102..a3b085aa 100644
--- a/openpgp/src/crypto/mod.rs
+++ b/openpgp/src/crypto/mod.rs
@@ -34,6 +34,7 @@ pub use self::asymmetric::{Signer, Decryptor, KeyPair};
pub(crate) 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 f7287f5d..89a02ad4 100644
--- a/openpgp/src/packet/key.rs
+++ b/openpgp/src/packet/key.rs
@@ -98,7 +98,11 @@ use crate::PublicKeyAlgorithm;
use crate::seal;
use crate::SymmetricAlgorithm;
use crate::HashAlgorithm;
-use crate::types::{Curve, Timestamp};
+use crate::types::{
+ AEADAlgorithm,
+ Curve,
+ Timestamp,
+};
use crate::crypto::S2K;
use crate::Result;
use crate::crypto::Password;
@@ -109,6 +113,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.
@@ -1789,6 +1795,8 @@ pub struct Encrypted {
s2k: S2K,
/// Symmetric algorithm used to encrypt the secret key material.
algo: SymmetricAlgorithm,
+ /// AEAD algorithm and IV used to encrypt the secret key material.
+ aead: Option<(AEADAlgorithm, Box<[u8]>)>,
/// Checksum method.
checksum: Option<mpi::SecretKeyChecksum>,
/// Encrypted MPIs prefixed with the IV.
@@ -1850,13 +1858,30 @@ impl Encrypted {
}
/// Creates a new encrypted key object.
+ pub fn new_aead(s2k: S2K,
+ sym_algo: SymmetricAlgorithm,
+ aead_algo: AEADAlgorithm,
+ aead_iv: Box<[u8]>,
+ ciphertext: Box<[u8]>)
+ -> Self
+ {
+ Encrypted {
+ s2k,
+ algo: sym_algo,
+ aead: Some((aead_algo, aead_iv)),
+ checksum: None,
+ ciphertext: Ok(ciphertext),
+ }
+ }
+
+ /// Creates a new encrypted key object.
pub(crate) fn new_raw(s2k: S2K, algo: SymmetricAlgorithm,
checksum: Option<mpi::SecretKeyChecksum>,
ciphertext: std::result::Result<Box<[u8]>,
Box<[u8]>>)
-> Self
{
- Encrypted { s2k, algo, checksum, ciphertext }
+ Encrypted { s2k, algo, aead: None, checksum, ciphertext }
}
/// Returns the key derivation mechanism.
@@ -1870,6 +1895,17 @@ impl Encrypted {
self.algo
}
+ /// Returns the AEAD algorithm used to encrypt the secret key
+ /// material.
+ pub fn aead_algo(&self) -> Option<AEADAlgorithm> {
+ self.aead.as_ref().map(|(a, _iv)| *a)
+ }
+
+ /// Returns the AEAD IV used to encrypt the secret key material.
+ pub fn aead_iv(&self) -> Option<&[u8]> {
+ self.aead.as_ref().map(|(_a, iv)| &iv[..])
+ }
+
/// Returns the checksum method used to protect the encrypted
/// secret key material, if any.
pub fn checksum(&self) -> Option<mpi::SecretKeyChecksum> {
@@ -1937,9 +1973,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()
+ }
}
}
@@ -2762,7 +2803,21 @@ FwPoSAbbsLkNS/iNN2MDGAVYvezYn2QZ
}
}
}
+ Ok(())
+ }
+ #[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 babdc192..7b479578 100644
--- a/openpgp/src/packet/key/conversions.rs
+++ b/openpgp/src/packet/key/conversions.rs
@@ -527,6 +527,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