summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/openssl/ecdh.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/crypto/backend/openssl/ecdh.rs')
-rw-r--r--openpgp/src/crypto/backend/openssl/ecdh.rs123
1 files changed, 123 insertions, 0 deletions
diff --git a/openpgp/src/crypto/backend/openssl/ecdh.rs b/openpgp/src/crypto/backend/openssl/ecdh.rs
new file mode 100644
index 00000000..662cc596
--- /dev/null
+++ b/openpgp/src/crypto/backend/openssl/ecdh.rs
@@ -0,0 +1,123 @@
+//! Elliptic Curve Diffie-Hellman.
+use std::convert::{TryFrom, TryInto};
+
+use crate::crypto::ecdh::{decrypt_unwrap, encrypt_wrap};
+use crate::crypto::mpi;
+use crate::crypto::mpi::{Ciphertext, SecretKeyMaterial};
+use crate::crypto::SessionKey;
+use crate::packet::{key, Key};
+use crate::types::Curve;
+use crate::{Error, Result};
+
+use openssl::bn::{BigNum, BigNumContext};
+use openssl::derive::Deriver;
+use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm};
+use openssl::pkey::PKey;
+
+/// Wraps a session key using Elliptic Curve Diffie-Hellman.
+pub fn encrypt<R>(
+ recipient: &Key<key::PublicParts, R>,
+ session_key: &SessionKey,
+) -> Result<Ciphertext>
+where
+ R: key::KeyRole,
+{
+ let (curve, q) = match recipient.mpis() {
+ mpi::PublicKey::ECDH { curve, q, .. } => (curve, q),
+ _ => return Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()),
+ };
+ if curve == &Curve::Cv25519 {
+ let public = q.decode_point(curve)?.0;
+
+ let public_key = PKey::public_key_from_raw_bytes(public, openssl::pkey::Id::X25519)?;
+
+ let key = PKey::generate_x25519()?;
+ let mut deriver = Deriver::new(&key)?;
+ deriver.set_peer(&public_key)?;
+
+ let secret = deriver.derive_to_vec()?.into();
+
+ let q = mpi::MPI::new_compressed_point(&key.raw_public_key()?);
+
+ return encrypt_wrap(recipient, session_key, q, &secret);
+ }
+
+ 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 recipient_key = EcKey::from_public_key(&group, &point)?;
+ let recipient_key = PKey::<_>::try_from(recipient_key)?;
+
+ let key = EcKey::generate(&group)?;
+
+ let q = mpi::MPI::new(&key.public_key().to_bytes(
+ &group,
+ PointConversionForm::COMPRESSED,
+ &mut ctx,
+ )?);
+
+ let key = PKey::<_>::try_from(key)?;
+ let mut deriver = Deriver::new(&key)?;
+ deriver.set_peer(&recipient_key)?;
+
+ let secret = deriver.derive_to_vec()?.into();
+
+ encrypt_wrap(recipient, session_key, q, &secret)
+}
+
+/// Unwraps a session key using Elliptic Curve Diffie-Hellman.
+pub fn decrypt<R>(
+ recipient: &Key<key::PublicParts, R>,
+ recipient_sec: &SecretKeyMaterial,
+ ciphertext: &Ciphertext,
+) -> Result<SessionKey>
+where
+ R: key::KeyRole,
+{
+ let (curve, scalar, e, q) = match (recipient.mpis(), recipient_sec, ciphertext) {
+ (
+ mpi::PublicKey::ECDH {
+ ref curve, ref q, ..
+ },
+ SecretKeyMaterial::ECDH { ref scalar },
+ Ciphertext::ECDH { ref e, .. },
+ ) => (curve, scalar, e, q),
+ _ => return Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()),
+ };
+
+ if curve == &Curve::Cv25519 {
+ let mut scalar = scalar.value_padded(32);
+ scalar.reverse();
+
+ let key = PKey::private_key_from_raw_bytes(&scalar[..], openssl::pkey::Id::X25519)?;
+
+ let public = e.decode_point(curve)?.0;
+ let public_key = PKey::public_key_from_raw_bytes(public, openssl::pkey::Id::X25519)?;
+
+ let mut deriver = Deriver::new(&key)?;
+ deriver.set_peer(&public_key)?;
+ let secret = deriver.derive_to_vec()?.into();
+
+ return decrypt_unwrap(recipient, &secret, ciphertext);
+ }
+
+ let nid = curve.try_into()?;
+ let group = EcGroup::from_curve_name(nid)?;
+ let mut ctx = BigNumContext::new()?;
+ let point = EcPoint::from_bytes(&group, e.value(), &mut ctx)?;
+
+ let public_point = EcPoint::from_bytes(&group, q.value(), &mut ctx)?;
+ let scalar = BigNum::from_slice(scalar.value())?;
+ let key = EcKey::from_private_components(&group, &scalar, &public_point)?;
+
+ let recipient_key = EcKey::from_public_key(&group, &point)?;
+ let recipient_key = PKey::<_>::try_from(recipient_key)?;
+
+ let key = PKey::<_>::try_from(key)?;
+ let mut deriver = Deriver::new(&key)?;
+ deriver.set_peer(&recipient_key)?;
+ let secret = deriver.derive_to_vec()?.into();
+
+ decrypt_unwrap(recipient, &secret, ciphertext)
+}