summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/rust/ecdh.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/crypto/backend/rust/ecdh.rs')
-rw-r--r--openpgp/src/crypto/backend/rust/ecdh.rs99
1 files changed, 99 insertions, 0 deletions
diff --git a/openpgp/src/crypto/backend/rust/ecdh.rs b/openpgp/src/crypto/backend/rust/ecdh.rs
new file mode 100644
index 00000000..519782f4
--- /dev/null
+++ b/openpgp/src/crypto/backend/rust/ecdh.rs
@@ -0,0 +1,99 @@
+//! Elliptic Curve Diffie-Hellman.
+
+use std::convert::TryInto;
+
+use rand::rngs::OsRng;
+
+use crate::{Error, Result};
+use crate::crypto::SessionKey;
+use crate::crypto::mem::Protected;
+use crate::crypto::ecdh::{encrypt_wrap, decrypt_unwrap};
+use crate::crypto::mpi::{self, Ciphertext, SecretKeyMaterial, MPI};
+use crate::packet::{key, Key};
+use crate::types::Curve;
+
+const CURVE25519_SIZE: usize = 32;
+
+/// Wraps a session key using Elliptic Curve Diffie-Hellman.
+#[allow(non_snake_case)]
+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()),
+ };
+
+ let (VB, shared) = match curve {
+ Curve::Cv25519 => {
+ use x25519_dalek::{EphemeralSecret, PublicKey};
+
+ // Decode the recipient's public key.
+ let R: [u8; CURVE25519_SIZE] = q.decode_point(curve)?.0.try_into()?;
+ let recipient_key = PublicKey::from(R);
+
+ // Generate a keypair and perform Diffie-Hellman.
+ let secret = EphemeralSecret::new(OsRng);
+ let public = PublicKey::from(&secret);
+ let shared = secret.diffie_hellman(&recipient_key);
+
+ // Encode our public key. We need to add an encoding
+ // octet in front of the key.
+ let mut VB = [0; 1 + CURVE25519_SIZE];
+ VB[0] = 0x40;
+ &mut VB[1..].copy_from_slice(public.as_bytes());
+ let VB = MPI::new(&VB);
+
+ // Encode the shared secret.
+ let shared: &[u8] = shared.as_bytes();
+ let shared = Protected::from(shared);
+
+ (VB, shared)
+ },
+ _ =>
+ return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
+ };
+
+ encrypt_wrap(recipient, session_key, VB, &shared)
+}
+
+/// Unwraps a session key using Elliptic Curve Diffie-Hellman.
+#[allow(non_snake_case)]
+pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>,
+ recipient_sec: &SecretKeyMaterial,
+ ciphertext: &Ciphertext)
+ -> Result<SessionKey>
+ where R: key::KeyRole
+{
+ let (curve, scalar, e) = match (recipient.mpis(), recipient_sec, ciphertext) {
+ (mpi::PublicKey::ECDH { ref curve, ..},
+ SecretKeyMaterial::ECDH { ref scalar, },
+ Ciphertext::ECDH { ref e, .. }) => (curve, scalar, e),
+ _ => return Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()),
+ };
+
+ let S: Protected = match curve {
+ Curve::Cv25519 => {
+ use x25519_dalek::{PublicKey, StaticSecret};
+
+ // Get the public part V of the ephemeral key.
+ let V: [u8; CURVE25519_SIZE] = e.decode_point(curve)?.0.try_into()?;
+ let V = PublicKey::from(V);
+
+ let mut scalar: [u8; CURVE25519_SIZE] =
+ scalar.value_padded(CURVE25519_SIZE).as_ref().try_into()?;
+ scalar.reverse();
+ let r = StaticSecret::from(scalar);
+
+ let secret = r.diffie_hellman(&V);
+ Vec::from(secret.to_bytes()).into()
+ },
+ _ => {
+ return Err(Error::UnsupportedEllipticCurve(curve.clone()).into());
+ },
+ };
+
+ decrypt_unwrap(recipient, &S, ciphertext)
+}