diff options
Diffstat (limited to 'openpgp/src/crypto/backend/rust/ecdh.rs')
-rw-r--r-- | openpgp/src/crypto/backend/rust/ecdh.rs | 99 |
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) +} |