summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2024-02-13 16:09:05 +0100
committerJustus Winter <justus@sequoia-pgp.org>2024-02-13 16:42:18 +0100
commit99a2aacc908ed5e2ec07cfbaead54f129dd2d2a6 (patch)
tree2d80895a418de8be86699612cdbed75f41c0af8d
parentaab11c2361223c9db3e1db722d7dcb95f3d940c0 (diff)
openpgp: Refactor Key4::generate_ecc.
- Move common code into a common frontend function.
-rw-r--r--openpgp/src/crypto/backend/botan/asymmetric.rs72
-rw-r--r--openpgp/src/crypto/backend/cng/asymmetric.rs92
-rw-r--r--openpgp/src/crypto/backend/fuzzing/asymmetric.rs6
-rw-r--r--openpgp/src/crypto/backend/nettle/asymmetric.rs80
-rw-r--r--openpgp/src/crypto/backend/openssl/asymmetric.rs67
-rw-r--r--openpgp/src/crypto/backend/rust/asymmetric.rs86
-rw-r--r--openpgp/src/packet/key.rs70
7 files changed, 163 insertions, 310 deletions
diff --git a/openpgp/src/crypto/backend/botan/asymmetric.rs b/openpgp/src/crypto/backend/botan/asymmetric.rs
index 5abd1c09..90be41c2 100644
--- a/openpgp/src/crypto/backend/botan/asymmetric.rs
+++ b/openpgp/src/crypto/backend/botan/asymmetric.rs
@@ -504,9 +504,11 @@ impl<R> Key4<SecretParts, R>
/// 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> {
- use crate::PublicKeyAlgorithm::*;
-
+ pub(crate) fn generate_ecc_backend(for_signing: bool, curve: Curve)
+ -> Result<(PublicKeyAlgorithm,
+ mpi::PublicKey,
+ mpi::SecretKeyMaterial)>
+ {
let mut rng = RandomNumberGenerator::new_userspace()?;
let hash = crate::crypto::ecdh::default_ecdh_kdf_hash(&curve);
let sym = crate::crypto::ecdh::default_ecdh_kek_cipher(&curve);
@@ -520,50 +522,12 @@ impl<R> Key4<SecretParts, R>
Err(Error::UnsupportedEllipticCurve(curve).into()),
};
- let (mpis, secret, pk_algo) = match (curve.clone(), for_signing) {
- (Curve::Ed25519, true) => {
- let secret = Privkey::create("Ed25519", "", &mut rng)?;
- let (public, secret) = secret.get_ed25519_key()?;
-
- let public_mpis = PublicKey::EdDSA {
- curve: Curve::Ed25519,
- q: MPI::new_compressed_point(&public),
- };
- let private_mpis = mpi::SecretKeyMaterial::EdDSA {
- scalar: secret.into(),
- };
-
- (public_mpis, private_mpis.into(), EdDSA)
- },
-
- (Curve::Cv25519, false) => {
- let secret = Privkey::create("Curve25519", "", &mut rng)?;
- let public = secret.pubkey()?.get_x25519_key()?;
- let mut secret: Protected = secret.get_x25519_key()?.into();
-
- // Clamp the scalar. X25519 does the clamping
- // implicitly, but OpenPGP's ECDH over Curve25519
- // requires the secret to be clamped.
- secret[0] &= 0b1111_1000;
- secret[31] &= !0b1000_0000;
- secret[31] |= 0b0100_0000;
-
- // Reverse the scalar. See
- // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html.
- secret.reverse();
-
- let public_mpis = PublicKey::ECDH {
- curve: Curve::Cv25519,
- q: MPI::new_compressed_point(&public),
- hash,
- sym,
- };
- let private_mpis = mpi::SecretKeyMaterial::ECDH {
- scalar: secret.into(),
- };
+ match (curve.clone(), for_signing) {
+ (Curve::Ed25519, true) =>
+ unreachable!("handled in Key4::generate_ecc"),
- (public_mpis, private_mpis.into(), ECDH)
- },
+ (Curve::Cv25519, false) =>
+ unreachable!("handled in Key4::generate_ecc"),
(Curve::NistP256, true) |
(Curve::NistP384, true) |
@@ -582,7 +546,7 @@ impl<R> Key4<SecretParts, R>
scalar: secret.get_field("x")?.try_into()?,
};
- (public_mpis, private_mpis.into(), ECDSA)
+ Ok((PublicKeyAlgorithm::ECDSA, public_mpis, private_mpis))
},
(Curve::NistP256, false) |
@@ -604,19 +568,11 @@ impl<R> Key4<SecretParts, R>
scalar: secret.get_field("x")?.try_into()?,
};
- (public_mpis, private_mpis.into(), ECDH)
+ Ok((PublicKeyAlgorithm::ECDH, public_mpis, private_mpis))
},
- (cv, _) => {
- return Err(Error::UnsupportedEllipticCurve(cv).into());
- }
- };
-
- Self::with_secret(
- crate::now(),
- pk_algo,
- mpis,
- secret)
+ _ => Err(Error::UnsupportedEllipticCurve(curve).into()),
+ }
}
}
diff --git a/openpgp/src/crypto/backend/cng/asymmetric.rs b/openpgp/src/crypto/backend/cng/asymmetric.rs
index 027725bc..809ab7f7 100644
--- a/openpgp/src/crypto/backend/cng/asymmetric.rs
+++ b/openpgp/src/crypto/backend/cng/asymmetric.rs
@@ -20,8 +20,6 @@ use crate::types::{Curve, HashAlgorithm};
use num_bigint_dig::{traits::ModInverse, BigInt, BigUint};
use win_crypto_ng as cng;
-const CURVE25519_SIZE: usize = 32;
-
impl TryFrom<&Protected> for Box<ed25519_dalek::SigningKey> {
type Error = anyhow::Error;
@@ -844,16 +842,21 @@ where
/// 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<Self> {
- use crate::PublicKeyAlgorithm::*;
-
+ pub(crate) fn generate_ecc_backend(for_signing: bool, curve: Curve)
+ -> Result<(PublicKeyAlgorithm,
+ mpi::PublicKey,
+ mpi::SecretKeyMaterial)>
+ {
use cng::asymmetric::{ecc, Export};
- use cng::asymmetric::{AsymmetricKey, AsymmetricAlgorithmId, Ecdh};
+ use cng::asymmetric::{AsymmetricKey, AsymmetricAlgorithmId};
+
+ match (curve.clone(), for_signing) {
+ (Curve::Ed25519, true) =>
+ unreachable!("handled in Key4::generate_ecc"),
- let hash = crate::crypto::ecdh::default_ecdh_kdf_hash(&curve);
- let sym = crate::crypto::ecdh::default_ecdh_kek_cipher(&curve);
+ (Curve::Cv25519, false) =>
+ unreachable!("handled in Key4::generate_ecc"),
- let (algo, public, private) = match (curve.clone(), for_signing) {
(Curve::NistP256, ..) | (Curve::NistP384, ..) | (Curve::NistP521, ..) => {
let cng_curve = match curve {
Curve::NistP256 => ecc::NamedCurve::NistP256,
@@ -881,72 +884,27 @@ where
let scalar = mpi::MPI::new(blob.d());
if for_signing {
- (
- ECDSA,
+ Ok((
+ PublicKeyAlgorithm::ECDSA,
mpi::PublicKey::ECDSA { curve, q },
mpi::SecretKeyMaterial::ECDSA { scalar: scalar.into() },
- )
+ ))
} else {
- (
- ECDH,
+ let hash =
+ crate::crypto::ecdh::default_ecdh_kdf_hash(&curve);
+ let sym =
+ crate::crypto::ecdh::default_ecdh_kek_cipher(&curve);
+
+ Ok((
+ PublicKeyAlgorithm::ECDH,
mpi::PublicKey::ECDH { curve, q, hash, sym },
mpi::SecretKeyMaterial::ECDH { scalar: scalar.into() },
- )
+ ))
}
},
- (Curve::Cv25519, false) => {
- let blob = AsymmetricKey::builder(Ecdh(ecc::Curve25519)).build()?.export()?;
-
- // Mark MPI as compressed point with 0x40 prefix. See
- // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2.
- let mut public = [0u8; 1 + CURVE25519_SIZE];
- public[0] = 0x40;
- public[1..].copy_from_slice(blob.x());
-
- // Reverse the scalar. See
- // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html.
- let mut private: Protected = blob.d().into();
- private.reverse();
-
- (
- ECDH,
- mpi::PublicKey::ECDH {
- curve,
- q: mpi::MPI::new(&public),
- hash,
- sym,
- },
- mpi::SecretKeyMaterial::ECDH { scalar: private.into() }
- )
- },
- (Curve::Ed25519, true) => {
- // CNG doesn't support EdDSA, use ed25519-dalek instead
- use ed25519_dalek::SigningKey;
-
- let mut rng = cng::random::RandomNumberGenerator::system_preferred();
- let key = SigningKey::generate(&mut rng);
-
- let secret: Protected = key.to_bytes().as_ref().into();
-
- // Mark MPI as compressed point with 0x40 prefix. See
- // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2.
- let mut compressed_public = [0u8; 1 + CURVE25519_SIZE];
- compressed_public[0] = 0x40;
- compressed_public[1..].copy_from_slice(key.verifying_key().as_bytes());
-
- (
- EdDSA,
- mpi::PublicKey::EdDSA { curve, q: mpi::MPI::new(&compressed_public) },
- mpi::SecretKeyMaterial::EdDSA { scalar: secret.into() },
- )
- },
- // TODO: Support Brainpool curves
- (curve, ..) => {
- return Err(Error::UnsupportedEllipticCurve(curve).into());
- }
- };
- Self::with_secret(crate::now(), algo, public, private.into())
+ _ => Err(Error::UnsupportedEllipticCurve(curve).into()),
+ }
}
}
diff --git a/openpgp/src/crypto/backend/fuzzing/asymmetric.rs b/openpgp/src/crypto/backend/fuzzing/asymmetric.rs
index 797795e9..025c856f 100644
--- a/openpgp/src/crypto/backend/fuzzing/asymmetric.rs
+++ b/openpgp/src/crypto/backend/fuzzing/asymmetric.rs
@@ -163,7 +163,11 @@ impl<R> Key4<SecretParts, R>
/// 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> {
+ pub(crate) fn generate_ecc_backend(for_signing: bool, curve: Curve)
+ -> Result<(PublicKeyAlgorithm,
+ mpi::PublicKey,
+ mpi::SecretKeyMaterial)>
+ {
Err(Error::InvalidOperation("not implemented".into()).into())
}
}
diff --git a/openpgp/src/crypto/backend/nettle/asymmetric.rs b/openpgp/src/crypto/backend/nettle/asymmetric.rs
index 405074a0..3202a67b 100644
--- a/openpgp/src/crypto/backend/nettle/asymmetric.rs
+++ b/openpgp/src/crypto/backend/nettle/asymmetric.rs
@@ -417,53 +417,19 @@ impl<R> Key4<SecretParts, R>
/// 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> {
- use crate::PublicKeyAlgorithm::*;
-
+ pub(crate) fn generate_ecc_backend(for_signing: bool, curve: Curve)
+ -> Result<(PublicKeyAlgorithm,
+ mpi::PublicKey,
+ mpi::SecretKeyMaterial)>
+ {
let mut rng = Yarrow::default();
- let hash = crate::crypto::ecdh::default_ecdh_kdf_hash(&curve);
- let sym = crate::crypto::ecdh::default_ecdh_kek_cipher(&curve);
-
- let (mpis, secret, pk_algo) = match (curve.clone(), for_signing) {
- (Curve::Ed25519, true) => {
- let mut public = [0; ed25519::ED25519_KEY_SIZE];
- let private: Protected =
- ed25519::private_key(&mut rng).into();
- ed25519::public_key(&mut public, &private)?;
-
- let public_mpis = PublicKey::EdDSA {
- curve: Curve::Ed25519,
- q: MPI::new_compressed_point(&public),
- };
- let private_mpis = mpi::SecretKeyMaterial::EdDSA {
- scalar: private.into(),
- };
- let sec = private_mpis.into();
- (public_mpis, sec, EdDSA)
- }
-
- (Curve::Cv25519, false) => {
- let (mut private, public) =
- super::Backend::x25519_generate_key()?;
-
- // 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_compressed_point(&public),
- hash,
- sym,
- };
- let private_mpis = mpi::SecretKeyMaterial::ECDH {
- scalar: private.into(),
- };
- let sec = private_mpis.into();
+ match (curve.clone(), for_signing) {
+ (Curve::Ed25519, true) =>
+ unreachable!("handled in Key4::generate_ecc"),
- (public_mpis, sec, ECDH)
- }
+ (Curve::Cv25519, false) =>
+ unreachable!("handled in Key4::generate_ecc"),
(Curve::NistP256, true) | (Curve::NistP384, true)
| (Curve::NistP521, true) => {
@@ -493,9 +459,8 @@ impl<R> Key4<SecretParts, R>
let private_mpis = mpi::SecretKeyMaterial::ECDSA{
scalar: private.as_bytes().into(),
};
- let sec = private_mpis.into();
- (public_mpis, sec, ECDSA)
+ Ok((PublicKeyAlgorithm::ECDSA, public_mpis, private_mpis))
}
(Curve::NistP256, false) | (Curve::NistP384, false)
@@ -524,28 +489,21 @@ impl<R> Key4<SecretParts, R>
let public = ecdh::point_mul_g(&private);
let (pub_x, pub_y) = public.as_bytes();
let public_mpis = mpi::PublicKey::ECDH{
- curve,
q: MPI::new_point(&pub_x, &pub_y, field_sz),
- hash,
- sym,
+ hash:
+ crate::crypto::ecdh::default_ecdh_kdf_hash(&curve),
+ sym:
+ crate::crypto::ecdh::default_ecdh_kek_cipher(&curve),
+ curve,
};
let private_mpis = mpi::SecretKeyMaterial::ECDH{
scalar: private.as_bytes().into(),
};
- let sec = private_mpis.into();
- (public_mpis, sec, ECDH)
+ Ok((PublicKeyAlgorithm::ECDH, public_mpis, private_mpis))
}
- (cv, _) => {
- return Err(Error::UnsupportedEllipticCurve(cv).into());
- }
- };
-
- Self::with_secret(
- crate::now(),
- pk_algo,
- mpis,
- secret)
+ _ => Err(Error::UnsupportedEllipticCurve(curve).into()),
+ }
}
}
diff --git a/openpgp/src/crypto/backend/openssl/asymmetric.rs b/openpgp/src/crypto/backend/openssl/asymmetric.rs
index 87d8623c..7da21b43 100644
--- a/openpgp/src/crypto/backend/openssl/asymmetric.rs
+++ b/openpgp/src/crypto/backend/openssl/asymmetric.rs
@@ -533,54 +533,11 @@ where
/// 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> {
- if for_signing && curve == Curve::Cv25519 {
- return Err(crate::Error::UnsupportedEllipticCurve(curve.clone()).into());
- }
-
- if !for_signing && curve == Curve::Ed25519 {
- return Err(crate::Error::UnsupportedEllipticCurve(curve.clone()).into());
- }
-
- if curve == Curve::Cv25519 || curve == Curve::Ed25519 {
- let key = if curve == Curve::Cv25519 {
- openssl::pkey::PKey::generate_x25519()?
- } else {
- openssl::pkey::PKey::generate_ed25519()?
- };
-
- let hash = crate::crypto::ecdh::default_ecdh_kdf_hash(&curve);
- let sym = crate::crypto::ecdh::default_ecdh_kek_cipher(&curve);
-
- let q = MPI::new_compressed_point(&key.raw_public_key()?);
- let mut scalar: Protected = key.raw_private_key().map(|key| key.into())?;
-
- if curve == Curve::Cv25519 {
- scalar.reverse();
- }
- let scalar = scalar.into();
-
- let (algo, public, private) = if for_signing {
- (
- PublicKeyAlgorithm::EdDSA,
- mpi::PublicKey::EdDSA { curve, q },
- mpi::SecretKeyMaterial::EdDSA { scalar },
- )
- } else {
- (
- PublicKeyAlgorithm::ECDH,
- mpi::PublicKey::ECDH {
- curve,
- q,
- hash,
- sym,
- },
- mpi::SecretKeyMaterial::ECDH { scalar },
- )
- };
- return Self::with_secret(crate::now(), algo, public, private.into());
- }
-
+ pub(crate) fn generate_ecc_backend(for_signing: bool, curve: Curve)
+ -> Result<(PublicKeyAlgorithm,
+ mpi::PublicKey,
+ mpi::SecretKeyMaterial)>
+ {
let nid = match curve {
Curve::NistP256 => Nid::X9_62_PRIME256V1,
Curve::NistP384 => Nid::SECP384R1,
@@ -602,14 +559,14 @@ where
)?);
let scalar = key.private_key().to_vec().into();
- let (algo, public, private) = if for_signing {
- (
+ if for_signing {
+ Ok((
PublicKeyAlgorithm::ECDSA,
mpi::PublicKey::ECDSA { curve, q },
mpi::SecretKeyMaterial::ECDSA { scalar },
- )
+ ))
} else {
- (
+ Ok((
PublicKeyAlgorithm::ECDH,
mpi::PublicKey::ECDH {
curve,
@@ -618,9 +575,7 @@ where
sym,
},
mpi::SecretKeyMaterial::ECDH { scalar },
- )
- };
-
- Self::with_secret(crate::now(), algo, public, private.into())
+ ))
+ }
}
}
diff --git a/openpgp/src/crypto/backend/rust/asymmetric.rs b/openpgp/src/crypto/backend/rust/asymmetric.rs
index 5bee9ecd..7df6ecf6 100644
--- a/openpgp/src/crypto/backend/rust/asymmetric.rs
+++ b/openpgp/src/crypto/backend/rust/asymmetric.rs
@@ -24,8 +24,6 @@ use crate::types::{Curve, HashAlgorithm, PublicKeyAlgorithm};
use super::GenericArrayExt;
-const CURVE25519_SIZE: usize = 32;
-
impl TryFrom<&Protected> for Box<ed25519_dalek::SigningKey> {
type Error = anyhow::Error;
@@ -557,62 +555,17 @@ impl<R> Key4<SecretParts, R>
/// 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> {
- let hash = crate::crypto::ecdh::default_ecdh_kdf_hash(&curve);
- let sym = crate::crypto::ecdh::default_ecdh_kek_cipher(&curve);
-
- let (algo, public, private) = match (&curve, for_signing) {
- (Curve::Ed25519, true) => {
- use ed25519_dalek::SigningKey;
-
- use rand::rngs::OsRng as OsRng;
-
- let key = SigningKey::generate(&mut OsRng);
-
- let secret: Protected = key.to_bytes().as_ref().into();
-
- // Mark MPI as compressed point with 0x40 prefix. See
- // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2.
- let mut compressed_public = [0u8; 1 + CURVE25519_SIZE];
- compressed_public[0] = 0x40;
- compressed_public[1..].copy_from_slice(key.verifying_key().as_bytes());
-
- (
- PublicKeyAlgorithm::EdDSA,
- mpi::PublicKey::EdDSA { curve, q: mpi::MPI::new(&compressed_public) },
- mpi::SecretKeyMaterial::EdDSA { scalar: secret.into() },
- )
- }
-
- (Curve::Cv25519, false) => {
- let (mut private, public) =
- super::Backend::x25519_generate_key()?;
-
- // x25519-dalek since v 2.0.0-rc.3 does not return clamped
- // integers from Static Secrets but clamps them on usage.
- // See: https://github.com/dalek-cryptography/x25519-dalek/blob/main/CHANGELOG.md#200-rc3
- //
- // Clamp the scalar. X25519 does the clamping implicitly, but
- // OpenPGP's ECDH over Curve25519 requires the secret to be
- // clamped.
- private[0] &= 0b1111_1000;
- private[31] &= !0b1000_0000;
- private[31] |= 0b0100_0000;
-
- private.reverse();
-
- let public_mpis = mpi::PublicKey::ECDH {
- curve: Curve::Cv25519,
- q: MPI::new_compressed_point(&public),
- hash,
- sym,
- };
- let private_mpis = mpi::SecretKeyMaterial::ECDH {
- scalar: private.into(),
- };
+ pub(crate) fn generate_ecc_backend(for_signing: bool, curve: Curve)
+ -> Result<(PublicKeyAlgorithm,
+ mpi::PublicKey,
+ mpi::SecretKeyMaterial)>
+ {
+ match (&curve, for_signing) {
+ (Curve::Ed25519, true) =>
+ unreachable!("handled in Key4::generate_ecc"),
- (PublicKeyAlgorithm::ECDH, public_mpis, private_mpis)
- }
+ (Curve::Cv25519, false) =>
+ unreachable!("handled in Key4::generate_ecc"),
(Curve::NistP256, true) => {
use p256::{EncodedPoint, SecretKey};
@@ -629,7 +582,7 @@ impl<R> Key4<SecretParts, R>
scalar: Vec::from(secret.to_bytes().as_slice()).into(),
};
- (PublicKeyAlgorithm::ECDSA, public_mpis, private_mpis)
+ Ok((PublicKeyAlgorithm::ECDSA, public_mpis, private_mpis))
},
(Curve::NistP256, false) => {
@@ -640,23 +593,22 @@ impl<R> Key4<SecretParts, R>
let public = EncodedPoint::from(secret.public_key());
let public_mpis = mpi::PublicKey::ECDH {
- curve,
q: MPI::new(public.as_bytes()),
- hash,
- sym,
+ hash:
+ crate::crypto::ecdh::default_ecdh_kdf_hash(&curve),
+ sym:
+ crate::crypto::ecdh::default_ecdh_kek_cipher(&curve),
+ curve,
};
let private_mpis = mpi::SecretKeyMaterial::ECDH {
scalar: Vec::from(secret.to_bytes().as_slice()).into(),
};
- (PublicKeyAlgorithm::ECDH, public_mpis, private_mpis)
+ Ok((PublicKeyAlgorithm::ECDH, public_mpis, private_mpis))
},
- _ => {
- return Err(Error::UnsupportedEllipticCurve(curve).into());
- }
- };
- Self::with_secret(crate::now(), algo, public, private.into())
+ _ => Err(Error::UnsupportedEllipticCurve(curve).into()),
+ }
}
}
diff --git a/openpgp/src/packet/key.rs b/openpgp/src/packet/key.rs
index 787c5101..626239fb 100644
--- a/openpgp/src/packet/key.rs
+++ b/openpgp/src/packet/key.rs
@@ -1213,6 +1213,76 @@ impl<R> Key4<SecretParts, R>
}.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. Likewise
+ /// `for_signing == false` and `curve == Ed25519` will produce an error.
+ pub fn generate_ecc(for_signing: bool, curve: Curve) -> Result<Self> {
+ use crate::crypto::backend::{Backend, interface::Asymmetric};
+
+ let (pk_algo, public, secret) = match (curve, for_signing) {
+ (Curve::Ed25519, true) => {
+ let (secret, public) = Backend::ed25519_generate_key()?;
+
+ (
+ PublicKeyAlgorithm::EdDSA,
+ mpi::PublicKey::EdDSA {
+ curve: Curve::Ed25519,
+ q: mpi::MPI::new_compressed_point(&public),
+ },
+ mpi::SecretKeyMaterial::EdDSA {
+ scalar: secret.into(),
+ },
+ )
+ },
+
+ (Curve::Cv25519, false) => {
+ let (mut secret, public) = Backend::x25519_generate_key()?;
+
+ // Clamp the X25519 secret key scalar.
+ //
+ // X25519 does the clamping implicitly, but OpenPGP's ECDH over
+ // Curve25519 requires the secret to be clamped. To increase
+ // compatibility with OpenPGP implementations that do not
+ // implicitly clamp the secrets before use, we do that before we
+ // store the secrets in OpenPGP data structures.
+ Backend::x25519_clamp_secret(&mut secret);
+
+ // Reverse the scalar.
+ //
+ // X25519 stores the secret as opaque byte string
+ // representing a little-endian scalar. OpenPGP's
+ // ECDH over Curve25519 on the other hand stores it as
+ // big-endian scalar, as was customary in OpenPGP.
+ // See
+ // https://lists.gnupg.org/pipermail/gnupg-devel/2018-February/033437.html.
+ secret.reverse();
+
+ (
+ PublicKeyAlgorithm::ECDH,
+ mpi::PublicKey::ECDH {
+ curve: Curve::Cv25519,
+ q: mpi::MPI::new_compressed_point(&public),
+ hash: crate::crypto::ecdh::default_ecdh_kdf_hash(
+ &Curve::Cv25519),
+ sym: crate::crypto::ecdh::default_ecdh_kek_cipher(
+ &Curve::Cv25519),
+ },
+ mpi::SecretKeyMaterial::ECDH {
+ scalar: secret.into(),
+ },
+ )
+ },
+
+ (curve, for_signing) =>
+ Self::generate_ecc_backend(for_signing, curve)?,
+ };
+
+ Self::with_secret(crate::now(), pk_algo, public, secret.into())
+ }
+
/// Generates a new DSA key with a