summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/openssl/asymmetric.rs
diff options
context:
space:
mode:
authorWiktor Kwapisiewicz <wiktor@metacode.biz>2022-10-03 11:51:41 +0200
committerWiktor Kwapisiewicz <wiktor@metacode.biz>2022-12-22 12:46:08 +0100
commit0d0d757f90b8b49010f998435a7e295cc0719ed2 (patch)
treee064d21b27c08b7e3371a5e51e30ba92da999381 /openpgp/src/crypto/backend/openssl/asymmetric.rs
parent31564c1c6e5b5ff4ff3596bb755ba2f7395f83df (diff)
openpgp: Add OpenSSL cryptographic backend.
- Adds the backend behind `crypto-openssl` feature. - Add CI configuration to run tests with the new backend. - See #333.
Diffstat (limited to 'openpgp/src/crypto/backend/openssl/asymmetric.rs')
-rw-r--r--openpgp/src/crypto/backend/openssl/asymmetric.rs642
1 files changed, 642 insertions, 0 deletions
diff --git a/openpgp/src/crypto/backend/openssl/asymmetric.rs b/openpgp/src/crypto/backend/openssl/asymmetric.rs
new file mode 100644
index 00000000..aef777bf
--- /dev/null
+++ b/openpgp/src/crypto/backend/openssl/asymmetric.rs
@@ -0,0 +1,642 @@
+use crate::Result;
+
+use crate::crypto::asymmetric::{Decryptor, KeyPair, Signer};
+use crate::crypto::mpi;
+use crate::crypto::mpi::{ProtectedMPI, MPI};
+use crate::crypto::mem::Protected;
+use crate::crypto::SessionKey;
+use crate::packet::key::{Key4, SecretParts};
+use crate::packet::{key, Key};
+use crate::types::SymmetricAlgorithm;
+use crate::types::{Curve, HashAlgorithm, PublicKeyAlgorithm};
+use std::convert::{TryFrom, TryInto};
+use std::time::SystemTime;
+
+use openssl::bn::{BigNum, BigNumRef, BigNumContext};
+use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm};
+use openssl::ecdsa::EcdsaSig;
+use openssl::nid::Nid;
+use openssl::pkey::PKey;
+use openssl::pkey_ctx::PkeyCtx;
+use openssl::rsa::{Padding, Rsa, RsaPrivateKeyBuilder};
+use openssl::sign::Signer as OpenSslSigner;
+use openssl::sign::Verifier;
+
+impl TryFrom<&ProtectedMPI> for BigNum {
+ type Error = anyhow::Error;
+ fn try_from(mpi: &ProtectedMPI) -> std::result::Result<BigNum, anyhow::Error> {
+ let mut bn = BigNum::new_secure()?;
+ bn.copy_from_slice(mpi.value())?;
+ Ok(bn)
+ }
+}
+
+impl From<&BigNumRef> for ProtectedMPI {
+ fn from(bn: &BigNumRef) -> Self {
+ bn.to_vec().into()
+ }
+}
+
+impl From<BigNum> for ProtectedMPI {
+ fn from(bn: BigNum) -> Self {
+ bn.to_vec().into()
+ }
+}
+
+impl From<BigNum> for MPI {
+ fn from(bn: BigNum) -> Self {
+ bn.to_vec().into()
+ }
+}
+
+impl TryFrom<&MPI> for BigNum {
+ type Error = anyhow::Error;
+ fn try_from(mpi: &MPI) -> std::result::Result<BigNum, anyhow::Error> {
+ Ok(BigNum::from_slice(mpi.value())?)
+ }
+}
+
+impl From<&BigNumRef> for MPI {
+ fn from(bn: &BigNumRef) -> Self {
+ bn.to_vec().into()
+ }
+}
+
+impl TryFrom<&Curve> for Nid {
+ type Error = crate::Error;
+ fn try_from(curve: &Curve) -> std::result::Result<Nid, crate::Error> {
+ Ok(match curve {
+ Curve::NistP256 => Nid::X9_62_PRIME256V1,
+ Curve::NistP384 => Nid::SECP384R1,
+ Curve::NistP521 => Nid::SECP521R1,
+ Curve::BrainpoolP256 => Nid::BRAINPOOL_P256R1,
+ Curve::BrainpoolP512 => Nid::BRAINPOOL_P512R1,
+ _ => return Err(crate::Error::UnsupportedEllipticCurve(curve.clone()).into()),
+ })
+ }
+}
+
+impl Signer for KeyPair {
+ fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
+ KeyPair::public(self)
+ }
+
+ fn sign(&mut self, hash_algo: HashAlgorithm, digest: &[u8]) -> Result<mpi::Signature> {
+ use crate::PublicKeyAlgorithm::*;
+ #[allow(deprecated)]
+ self.secret().map(
+ |secret| match (self.public().pk_algo(), self.public().mpis(), secret) {
+ (
+ RSAEncryptSign,
+ mpi::PublicKey::RSA { e, n },
+ mpi::SecretKeyMaterial::RSA { p, q, d, .. },
+ )
+ | (
+ RSASign,
+ mpi::PublicKey::RSA { e, n },
+ mpi::SecretKeyMaterial::RSA { p, q, d, .. },
+ ) => {
+ let key =
+ RsaPrivateKeyBuilder::new(n.try_into()?, e.try_into()?, d.try_into()?)?
+ .set_factors(p.try_into()?, q.try_into()?)?
+ .build();
+
+ let key = PKey::from_rsa(key)?;
+
+ let mut signature: Vec<u8> = vec![];
+
+ const MAX_OID_SIZE: usize = 20;
+ let mut v = Vec::with_capacity(MAX_OID_SIZE + digest.len());
+ v.extend(hash_algo.oid()?);
+ v.extend(digest);
+
+ let mut ctx = PkeyCtx::new(&key)?;
+ ctx.sign_init()?;
+ ctx.sign_to_vec(&v, &mut signature)?;
+
+ Ok(mpi::Signature::RSA {
+ s: signature.into(),
+ })
+ }
+ (
+ PublicKeyAlgorithm::DSA,
+ mpi::PublicKey::DSA { p, q, g, y },
+ mpi::SecretKeyMaterial::DSA { x },
+ ) => {
+ use openssl::dsa::{Dsa, DsaSig};
+ let dsa = Dsa::from_private_components(
+ p.try_into()?,
+ q.try_into()?,
+ g.try_into()?,
+ x.try_into()?,
+ y.try_into()?,
+ )?;
+ let key: PKey<_> = dsa.try_into()?;
+ let mut ctx = PkeyCtx::new(&key)?;
+ ctx.sign_init()?;
+ let mut signature = vec![];
+ ctx.sign_to_vec(&digest, &mut signature)?;
+ let signature = DsaSig::from_der(&signature)?;
+
+ Ok(mpi::Signature::DSA {
+ r: signature.r().to_vec().into(),
+ s: signature.s().to_vec().into(),
+ })
+ }
+ (
+ PublicKeyAlgorithm::ECDSA,
+ mpi::PublicKey::ECDSA { curve, q },
+ mpi::SecretKeyMaterial::ECDSA { scalar },
+ ) => {
+ let nid = curve.try_into()?;
+ let group = EcGroup::from_curve_name(nid)?;
+ let mut ctx = BigNumContext::new()?;
+ let point = EcPoint::from_bytes(&group, q.value(), &mut ctx)?;
+ let mut private = BigNum::new_secure()?;
+ private.copy_from_slice(scalar.value())?;
+ let key = EcKey::from_private_components(&group, &private, &point)?;
+ let sig = EcdsaSig::sign(digest, &key)?;
+ Ok(mpi::Signature::ECDSA {
+ r: sig.r().into(),
+ s: sig.s().into(),
+ })
+ }
+
+ (
+ EdDSA,
+ mpi::PublicKey::EdDSA { curve, .. },
+ mpi::SecretKeyMaterial::EdDSA { scalar },
+ ) => match curve {
+ Curve::Ed25519 => {
+ let scalar = scalar.value_padded(32);
+
+ let key =
+ PKey::private_key_from_raw_bytes(&scalar, openssl::pkey::Id::ED25519)?;
+
+ let mut signer = OpenSslSigner::new_without_digest(&key)?;
+ let signature = signer.sign_oneshot_to_vec(digest)?;
+
+ // https://tools.ietf.org/html/rfc8032#section-5.1.6
+ let (r, s) = signature.split_at(signature.len() / 2);
+ Ok(mpi::Signature::EdDSA {
+ r: r.to_vec().into(),
+ s: s.to_vec().into(),
+ })
+ }
+ _ => Err(crate::Error::UnsupportedEllipticCurve(curve.clone()).into()),
+ },
+
+ (pk_algo, _, _) => Err(crate::Error::InvalidOperation(format!(
+ "unsupported combination of algorithm {:?}, key {:?}, \
+ and secret key {:?} by OpenSSL backend",
+ pk_algo,
+ self.public(),
+ self.secret()
+ ))
+ .into()),
+ },
+ )
+ }
+}
+
+impl Decryptor for KeyPair {
+ fn public(&self) -> &Key<key::PublicParts, key::UnspecifiedRole> {
+ KeyPair::public(self)
+ }
+
+ fn decrypt(
+ &mut self,
+ ciphertext: &mpi::Ciphertext,
+ _plaintext_len: Option<usize>,
+ ) -> Result<SessionKey> {
+ use crate::crypto::mpi::PublicKey;
+
+ self.secret().map(|secret| {
+ Ok(match (self.public().mpis(), secret, ciphertext) {
+ (
+ PublicKey::RSA { ref e, ref n },
+ mpi::SecretKeyMaterial::RSA {
+ ref p,
+ ref q,
+ ref d,
+ ..
+ },
+ mpi::Ciphertext::RSA { ref c },
+ ) => {
+ let key =
+ RsaPrivateKeyBuilder::new(n.try_into()?, e.try_into()?, d.try_into()?)?
+ .set_factors(p.try_into()?, q.try_into()?)?
+ .build();
+
+ let mut buf: Protected = vec![0; key.size().try_into()?].into();
+ let encrypted_len = key.private_decrypt(c.value(), &mut buf, Padding::PKCS1)?;
+ buf[..encrypted_len].into()
+ }
+
+ (
+ PublicKey::ECDH { .. },
+ mpi::SecretKeyMaterial::ECDH { .. },
+ mpi::Ciphertext::ECDH { .. },
+ ) => crate::crypto::ecdh::decrypt(self.public(), secret, ciphertext)?,
+
+ (public, secret, ciphertext) => {
+ return Err(crate::Error::InvalidOperation(format!(
+ "unsupported combination of key pair {:?}/{:?} \
+ and ciphertext {:?}",
+ public, secret, ciphertext
+ ))
+ .into())
+ }
+ })
+ })
+ }
+}
+
+impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> {
+ /// Encrypts the given data with this key.
+ pub fn encrypt(&self, data: &SessionKey) -> Result<mpi::Ciphertext> {
+ use PublicKeyAlgorithm::*;
+ #[allow(deprecated)]
+ match self.pk_algo() {
+ RSAEncryptSign | RSAEncrypt => match self.mpis() {
+ mpi::PublicKey::RSA { e, n } => {
+ // The ciphertext has the length of the modulus.
+ let ciphertext_len = n.value().len();
+ if data.len() + 11 > ciphertext_len {
+ return Err(crate::Error::InvalidArgument(
+ "Plaintext data too large".into(),
+ )
+ .into());
+ }
+
+ let e = BigNum::from_slice(e.value())?;
+ let n = BigNum::from_slice(n.value())?;
+ let rsa = Rsa::<openssl::pkey::Public>::from_public_components(n, e)?;
+
+ // The ciphertext has the length of the modulus.
+ let mut buf = vec![0; rsa.size().try_into()?];
+ rsa.public_encrypt(data, &mut buf, Padding::PKCS1)?;
+ Ok(mpi::Ciphertext::RSA {
+ c: buf.into(),
+ })
+ }
+ pk => Err(crate::Error::MalformedPacket(format!(
+ "Key: Expected RSA public key, got {:?}",
+ pk
+ ))
+ .into()),
+ },
+ ECDH => crate::crypto::ecdh::encrypt(self.parts_as_public(), data),
+ algo => Err(crate::Error::UnsupportedPublicKeyAlgorithm(algo).into()),
+ }
+ }
+
+ /// Verifies the given signature.
+ pub fn verify(
+ &self,
+ sig: &mpi::Signature,
+ hash_algo: HashAlgorithm,
+ digest: &[u8],
+ ) -> Result<()> {
+ let ok = match (self.mpis(), sig) {
+ (mpi::PublicKey::RSA { e, n }, mpi::Signature::RSA { s }) => {
+ let e = BigNum::from_slice(e.value())?;
+ let n = BigNum::from_slice(n.value())?;
+ let keypair = Rsa::<openssl::pkey::Public>::from_public_components(n, e)?;
+ let keypair = PKey::from_rsa(keypair)?;
+
+ let signature = s.value();
+ let mut v = vec![];
+ v.extend(hash_algo.oid()?);
+ v.extend(digest);
+
+ let mut ctx = PkeyCtx::new(&keypair)?;
+ ctx.verify_init()?;
+ ctx.verify(&v, signature)?
+ }
+ (mpi::PublicKey::DSA { p, q, g, y }, mpi::Signature::DSA { r, s }) => {
+ use openssl::dsa::{Dsa, DsaSig};
+ let dsa = Dsa::from_public_components(
+ p.try_into()?,
+ q.try_into()?,
+ g.try_into()?,
+ y.try_into()?,
+ )?;
+ let key: PKey<_> = dsa.try_into()?;
+ let r = r.try_into()?;
+ let s = s.try_into()?;
+ let signature = DsaSig::from_private_components(r, s)?;
+ let mut ctx = PkeyCtx::new(&key)?;
+ ctx.verify_init()?;
+ ctx.verify(&digest, &signature.to_der()?)?
+ }
+ (mpi::PublicKey::EdDSA { curve, q }, mpi::Signature::EdDSA { r, s }) => match curve {
+ Curve::Ed25519 => {
+ let public = q.decode_point(&Curve::Ed25519)?.0;
+
+ let key = PKey::public_key_from_raw_bytes(public, openssl::pkey::Id::ED25519)?;
+
+ const SIGNATURE_LENGTH: usize = 64;
+
+ // ed25519 expects full-sized signatures but OpenPGP allows
+ // for stripped leading zeroes, pad each part with zeroes.
+ let mut sig_bytes = [0u8; SIGNATURE_LENGTH];
+
+ // We need to zero-pad them at the front, because
+ // the MPI encoding drops leading zero bytes.
+ let half = SIGNATURE_LENGTH / 2;
+ sig_bytes[..half].copy_from_slice(&r.value_padded(half)?);
+ sig_bytes[half..].copy_from_slice(&s.value_padded(half)?);
+
+ let mut verifier = Verifier::new_without_digest(&key)?;
+ verifier.verify_oneshot(&sig_bytes, digest)?
+ }
+ _ => return Err(crate::Error::UnsupportedEllipticCurve(curve.clone()).into()),
+ },
+ (mpi::PublicKey::ECDSA { curve, q }, mpi::Signature::ECDSA { s, r }) => {
+ let nid = curve.try_into()?;
+ let group = EcGroup::from_curve_name(nid)?;
+ let mut ctx = BigNumContext::new()?;
+ let point = EcPoint::from_bytes(&group, q.value(), &mut ctx)?;
+ let key = EcKey::from_public_key(&group, &point)?;
+ let sig = EcdsaSig::from_private_components(
+ r.try_into()?,
+ s.try_into()?,
+ )?;
+ sig.verify(digest, &key)?
+ }
+ _ => {
+ return Err(crate::Error::MalformedPacket(format!(
+ "unsupported combination of key {} and signature {:?}.",
+ self.pk_algo(),
+ sig
+ ))
+ .into())
+ }
+ };
+
+ if ok {
+ Ok(())
+ } else {
+ Err(crate::Error::ManipulatedMessage.into())
+ }
+ }
+}
+
+impl<R> Key4<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>>,
+ {
+ let key = PKey::private_key_from_raw_bytes(private_key, openssl::pkey::Id::X25519)?;
+ let public_key = key.raw_public_key()?;
+
+ let mut private_key: Protected = key.raw_private_key().map(|key| key.into())?;
+ private_key.reverse();
+
+ use crate::crypto::ecdh;
+ Self::with_secret(
+ ctime.into().unwrap_or_else(crate::now),
+ PublicKeyAlgorithm::ECDH,
+ mpi::PublicKey::ECDH {
+ curve: Curve::Cv25519,
+ hash: hash
+ .into()
+ .unwrap_or_else(|| ecdh::default_ecdh_kdf_hash(&Curve::Cv25519)),
+ sym: sym
+ .into()
+ .unwrap_or_else(|| ecdh::default_ecdh_kek_cipher(&Curve::Cv25519)),
+ q: MPI::new_compressed_point(&public_key),
+ },
+ mpi::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<T>(private_key: &[u8], ctime: T) -> Result<Self>
+ where
+ T: Into<Option<SystemTime>>,
+ {
+ let key = PKey::private_key_from_raw_bytes(private_key, openssl::pkey::Id::ED25519)?;
+ let public_key = key.raw_public_key()?;
+
+ Self::with_secret(
+ ctime.into().unwrap_or_else(crate::now),
+ PublicKeyAlgorithm::EdDSA,
+ mpi::PublicKey::EdDSA {
+ curve: Curve::Ed25519,
+ q: public_key.into(),
+ },
+ mpi::SecretKeyMaterial::EdDSA {
+ scalar: mpi::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.
+ #[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>>,
+ {
+ // RFC 4880: `p < q`
+ let (p, q) = if p < q { (p, q) } else { (q, p) };
+
+ let mut big_p = BigNum::new_secure()?;
+ big_p.copy_from_slice(p)?;
+ let mut big_q = BigNum::new_secure()?;
+ big_q.copy_from_slice(q)?;
+ let n = &big_p * &big_q;
+
+ let mut one = BigNum::new_secure()?;
+ one.copy_from_slice(&[1])?;
+ let big_phi = &(&big_p - &one) * &(&big_q - &one);
+
+ let mut ctx = BigNumContext::new_secure()?;
+
+ let mut e = BigNum::new_secure()?;
+ let mut d_bn = BigNum::new_secure()?;
+ d_bn.copy_from_slice(d)?;
+ e.mod_inverse(&d_bn, &big_phi, &mut ctx)?; // e ≡ d⁻¹ (mod 𝜙)
+
+ let mut u = BigNum::new_secure()?;
+ u.mod_inverse(&big_p, &big_q, &mut ctx)?; // RFC 4880: u ≡ p⁻¹ (mod q)
+
+ Self::with_secret(
+ ctime.into().unwrap_or_else(crate::now),
+ PublicKeyAlgorithm::RSAEncryptSign,
+ mpi::PublicKey::RSA {
+ e: e.into(),
+ n: n.into(),
+ },
+ mpi::SecretKeyMaterial::RSA {
+ d: d_bn.into(),
+ p: mpi::MPI::new(p).into(),
+ q: mpi::MPI::new(q).into(),
+ u: u.into(),
+ }
+ .into(),
+ )
+ }
+
+ /// Generates a new RSA key with a public modulos of size `bits`.
+ #[allow(clippy::many_single_char_names)]
+ pub fn generate_rsa(bits: usize) -> Result<Self> {
+ let key = Rsa::generate(bits.try_into()?)?;
+ let e = key.e();
+ let n = key.n();
+ let d = key.d();
+ let p = key
+ .p()
+ .ok_or_else(|| crate::Error::InvalidOperation("p".into()))?;
+ let q = key
+ .q()
+ .ok_or_else(|| crate::Error::InvalidOperation("q".into()))?;
+
+ let mut ctx = BigNumContext::new_secure()?;
+ let mut u = BigNum::new_secure()?;
+ u.mod_inverse(p, q, &mut ctx)?;
+
+ Self::with_secret(
+ crate::now(),
+ PublicKeyAlgorithm::RSAEncryptSign,
+ mpi::PublicKey::RSA {
+ e: e.into(),
+ n: n.into(),
+ },
+ mpi::SecretKeyMaterial::RSA {
+ d: d.into(),
+ p: p.into(),
+ q: q.into(),
+ u: u.into(),
+ }
+ .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> {
+ 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());
+ }
+
+ let nid = match curve {
+ Curve::NistP256 => Nid::X9_62_PRIME256V1,
+ Curve::NistP384 => Nid::SECP384R1,
+ Curve::NistP521 => Nid::SECP521R1,
+ _ => return Err(crate::Error::UnsupportedEllipticCurve(curve.clone()).into()),
+ };
+
+ let group = EcGroup::from_curve_name(nid)?;
+ let key = EcKey::generate(&group)?;
+
+ let hash = crate::crypto::ecdh::default_ecdh_kdf_hash(&curve);
+ let sym = crate::crypto::ecdh::default_ecdh_kek_cipher(&curve);
+ let mut ctx = BigNumContext::new()?;
+
+ let q = MPI::new(&key.public_key().to_bytes(
+ &group,
+ PointConversionForm::COMPRESSED,
+ &mut ctx,
+ )?);
+ let scalar = key.private_key().to_vec().into();
+
+ let (algo, public, private) = if for_signing {
+ (
+ PublicKeyAlgorithm::ECDSA,
+ mpi::PublicKey::ECDSA { curve, q },
+ mpi::SecretKeyMaterial::ECDSA { scalar },
+ )
+ } else {
+ (
+ PublicKeyAlgorithm::ECDH,
+ mpi::PublicKey::ECDH {
+ curve,
+ q,
+ hash,
+ sym,
+ },
+ mpi::SecretKeyMaterial::ECDH { scalar },
+ )
+ };
+
+ Self::with_secret(crate::now(), algo, public, private.into())
+ }
+}