Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ members = [
"test-utils",
"crates/primitives/aead",
"crates/primitives/digest",
"crates/testing/kats",
"crates/testing/kats", "crates/primitives/ecdh",
]

[workspace.package]
Expand Down
22 changes: 22 additions & 0 deletions crates/primitives/ecdh/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
name = "libcrux-ecdh-new"
readme.workspace = true
repository.workspace = true
version.workspace = true

[dependencies]
libcrux-curve25519 = { version = "0.0.3", path = "../../../curve25519", optional = true }
libcrux-p256 = { version = "0.0.3", path = "../../../p256", optional = true }
libcrux-traits = { version = "0.0.3", path = "../../../traits", optional = true }

[features]
default = ["curve25519", "p256"]
p256 = ["dep:libcrux-p256", "dep:libcrux-traits"]
curve25519 = ["dep:libcrux-curve25519", "dep:libcrux-traits"]

[dev-dependencies]
rand = "0.9"
91 changes: 91 additions & 0 deletions crates/primitives/ecdh/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! # Elliptic Curve Diffie-Hellman (ECDH) key exchange
//!
//! This crate provides a uniform API for elliptic curve Diffie-Hellman
//! key exchange over the following curves:
//!
//! - NIST P-256
//! - Curve25519
//!
//! *TODO*: Explain error types, different APIs.
//!
//! Usage example:
//! ```rust
//! use libcrux_ecdh_new::curve25519::{X25519Pair, RAND_LEN};
//!
//! use rand::RngCore;
//! let mut rng = rand::rng();
//!
//! let mut randomness_a = [0u8; RAND_LEN];
//! let mut randomness_b = [0u8; RAND_LEN];
//!
//! rng.fill_bytes(&mut randomness_a);
//! let x25519_pair_a = X25519Pair::generate(&randomness_a).unwrap();
//!
//! rng.fill_bytes(&mut randomness_b);
//! let x25519_pair_b = X25519Pair::generate(&randomness_b).unwrap();
//!
//! let derived_a = x25519_pair_a.derive_ecdh(x25519_pair_b.public()).unwrap();
//! let derived_b = x25519_pair_b.derive_ecdh(x25519_pair_a.public()).unwrap();
//!
//! assert_eq!(derived_a, derived_b);
//! ```

#[cfg(feature = "p256")]
pub mod p256 {
//! This module provides an API for ECDH over NIST-P256.
//!
//! ```rust
//! use libcrux_ecdh_new::p256::{P256Pair, RAND_LEN};
//! use rand::RngCore;
//!
//! let mut rng = rand::rng();
//!
//! let mut randomness_a = [0u8; RAND_LEN];
//! let mut randomness_b = [0u8; RAND_LEN];
//!
//! rng.fill_bytes(&mut randomness_a);
//! let p256_pair_a = P256Pair::generate(&randomness_a).unwrap();
//!
//! rng.fill_bytes(&mut randomness_b);
//! let p256_pair_b = P256Pair::generate(&randomness_b).unwrap();
//!
//! let derived_a = p256_pair_a.derive_ecdh(p256_pair_b.public()).unwrap();
//! let derived_b = p256_pair_b.derive_ecdh(p256_pair_a.public()).unwrap();
//!
//! assert_eq!(derived_a, derived_b);
//! ```
pub use libcrux_p256::ecdh_api::*;

/// Access low level ECDH APIs via this struct.
pub use libcrux_p256::P256;
}

#[cfg(feature = "curve25519")]
pub mod curve25519 {
//! This module provides an API for ECDH over Curve25519.
//!
//! ```rust
//! use libcrux_ecdh_new::curve25519::{X25519Pair, RAND_LEN};
//!
//! use rand::RngCore;
//! let mut rng = rand::rng();
//!
//! let mut randomness_a = [0u8; RAND_LEN];
//! let mut randomness_b = [0u8; RAND_LEN];
//!
//! rng.fill_bytes(&mut randomness_a);
//! let x25519_pair_a = X25519Pair::generate(&randomness_a).unwrap();
//!
//! rng.fill_bytes(&mut randomness_b);
//! let x25519_pair_b = X25519Pair::generate(&randomness_b).unwrap();
//!
//! let derived_a = x25519_pair_a.derive_ecdh(x25519_pair_b.public()).unwrap();
//! let derived_b = x25519_pair_b.derive_ecdh(x25519_pair_a.public()).unwrap();
//!
//! assert_eq!(derived_a, derived_b);
//! ```
pub use libcrux_curve25519::ecdh_api::*;

/// Access low level ECDH APIs via this struct.
pub use libcrux_curve25519::X25519;
}
14 changes: 11 additions & 3 deletions curve25519/src/ecdh_api.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
use libcrux_traits::ecdh::typed_owned::Pair;
pub use libcrux_traits::ecdh::{arrayref::EcdhArrayref, owned::EcdhOwned, slice::EcdhSlice};

use crate::clamp;

use super::{DK_LEN, EK_LEN, X25519};

const RAND_LEN: usize = DK_LEN;
const SECRET_LEN: usize = DK_LEN;
const PUBLIC_LEN: usize = EK_LEN;
/// Number of bytes of randomness required to generate an ECDH secret.
pub const RAND_LEN: usize = DK_LEN;
/// Length in bytes of an ECDH secret value.
pub const SECRET_LEN: usize = DK_LEN;
/// Length in bytes of an ECDH public value.
pub const PUBLIC_LEN: usize = EK_LEN;

use libcrux_secrets::{Classify, Declassify, DeclassifyRef, DeclassifyRefMut, U8};

/// A corresponding pair of ECDH public and secret values over Curve25519.
pub type X25519Pair = Pair<X25519>;

impl libcrux_traits::ecdh::arrayref::EcdhArrayref<RAND_LEN, SECRET_LEN, PUBLIC_LEN> for X25519 {
fn generate_secret(
secret: &mut [U8; SECRET_LEN],
Expand Down Expand Up @@ -58,3 +65,4 @@ impl libcrux_traits::ecdh::arrayref::EcdhArrayref<RAND_LEN, SECRET_LEN, PUBLIC_L
}

libcrux_traits::ecdh::slice::impl_ecdh_slice_trait!(X25519 => RAND_LEN, SECRET_LEN, PUBLIC_LEN);
libcrux_traits::ecdh::typed_owned::impl_ecdh_typed_owned!(X25519 => RAND_LEN, SECRET_LEN, PUBLIC_LEN);
14 changes: 11 additions & 3 deletions p256/src/ecdh_api.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
use super::{POINT_LEN, SCALAR_LEN};

const RAND_LEN: usize = SCALAR_LEN;
const SECRET_LEN: usize = SCALAR_LEN;
const PUBLIC_LEN: usize = POINT_LEN;
/// Number of bytes of randomness required to generate an ECDH secret.
pub const RAND_LEN: usize = SCALAR_LEN;
/// Length in bytes of an ECDH secret value.
pub const SECRET_LEN: usize = SCALAR_LEN;
/// Length in bytes of an ECDH public value.
pub const PUBLIC_LEN: usize = POINT_LEN;

use libcrux_traits::ecdh::typed_owned::Pair;
pub use libcrux_traits::ecdh::{arrayref::EcdhArrayref, owned::EcdhOwned, slice::EcdhSlice};

use libcrux_secrets::{Declassify, DeclassifyRef, DeclassifyRefMut, U8};

/// A corresponding pair of ECDH public and secret values over P256.
pub type P256Pair = Pair<super::P256>;

impl libcrux_traits::ecdh::arrayref::EcdhArrayref<RAND_LEN, SECRET_LEN, PUBLIC_LEN>
for super::P256
{
Expand Down Expand Up @@ -63,3 +70,4 @@ impl libcrux_traits::ecdh::arrayref::EcdhArrayref<RAND_LEN, SECRET_LEN, PUBLIC_L
}

libcrux_traits::ecdh::slice::impl_ecdh_slice_trait!(super::P256 => RAND_LEN, SECRET_LEN, PUBLIC_LEN);
libcrux_traits::ecdh::typed_owned::impl_ecdh_typed_owned!(super::P256 => RAND_LEN, SECRET_LEN, PUBLIC_LEN);
2 changes: 2 additions & 0 deletions traits/src/ecdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
pub mod arrayref;
pub mod owned;
pub mod slice;

pub mod typed_owned;
4 changes: 3 additions & 1 deletion traits/src/ecdh/owned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
//! returns owned arrays.

use super::arrayref;
use super::arrayref::{DeriveError, GenerateSecretError, SecretToPublicError, ValidateSecretError};
pub use super::arrayref::{
DeriveError, GenerateSecretError, SecretToPublicError, ValidateSecretError,
};

use libcrux_secrets::{Classify, U8};

Expand Down
147 changes: 147 additions & 0 deletions traits/src/ecdh/typed_owned.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
//! This module provides a strongly typed ECDH API in terms of pairs
//! of corresponding ECDH secret and public values.

use crate::ecdh::owned::{self, GenerateSecretError, SecretToPublicError};

/// The types associated with an ECDH implementation.
pub trait EcdhTypes {
type Secret;
type Public;
type Randomness;
type Derived;
}

/// An pair of corresponding ECDH secret and public values, generic
/// over the underlying elliptic curve.
pub struct Pair<Algorithm: EcdhTypes> {
secret: Algorithm::Secret,
public: Algorithm::Public,
}

impl<Algorithm: Ecdh> Pair<Algorithm> {
/// Generate a new pair of ECDH secret and public values.
pub fn generate(rand: &Algorithm::Randomness) -> Result<Self, owned::GenerateSecretError> {
let (public, secret) = Algorithm::generate_pair(&rand)?;
Ok(Pair { secret, public })
}

/// Returns the secret component of the pair.
pub fn secret(&self) -> &Algorithm::Secret {
&self.secret
}

/// Returns the public component of the pair.
pub fn public(&self) -> &Algorithm::Public {
&self.public
}

/// Derive an ECDH shared secret value from the pairs secret value
/// and a given ECDH public value.
pub fn derive_ecdh(
&self,
public: &Algorithm::Public,
) -> Result<Algorithm::Derived, owned::DeriveError> {
Algorithm::derive_ecdh(public, self.secret())
}
}

/// Trait implementing ECDH operations over a given elliptic curve.
///
/// Mostly for `libcrux`-internal use.
pub trait Ecdh: EcdhTypes + Sized {
/// Generate a Diffie-Hellman secret value.
/// It is the responsibility of the caller to ensure that the `rand` argument is actually
/// random.
fn generate_secret(rand: &Self::Randomness) -> Result<Self::Secret, GenerateSecretError>;

/// Derive a Diffie-Hellman public value from a secret value.
fn secret_to_public(secret: &Self::Secret) -> Result<Self::Public, SecretToPublicError>;

/// Generate a Diffie-Hellman secret value and derive the
/// corresponding public value in one step.
fn generate_pair(
rand: &Self::Randomness,
) -> Result<(Self::Public, Self::Secret), GenerateSecretError> {
let secret = Self::generate_secret(rand)?;
let public = Self::secret_to_public(&secret).map_err(|_| GenerateSecretError::Unknown)?;
Ok((public, secret))
}

/// Derive a Diffie-Hellman shared secret from a public and a
/// secret value.
///
/// This value is NOT (!) safe for use as a key and needs to be processed in a round of key
/// derivation, to ensure both that the output is uniformly random and that unkown key share
/// attacks can not happen.
fn derive_ecdh(
public: &Self::Public,
secret: &Self::Secret,
) -> Result<Self::Derived, owned::DeriveError>;

/// Check the validity of a Diffie-Hellman secret value.
fn validate_secret(secret: &Self::Secret) -> Result<(), owned::ValidateSecretError>;
}

#[macro_export]
/// This macro implements the traits `ecdh::typed_owned::EcdhTypes` and `ecdh::typed_owned::Ecdh` from an
/// implementation of the `ecdh::owned::EcdhOwned` trait.
///
/// - `$ty` should provide an underlying implementation of `ecdh::owned::EcdhOwned`
/// - `$randomness_len` should be the length in bytes of randomness for ECDH secret
/// generation in the underlying implementation
/// - `$secret_len` should be the length in bytes of ECDH secret values in
/// the underlying implementation
/// - `$public_len` should be the length in bytes of ECDH public values in
/// the underlying implementation (this is also the length of ECDH
/// shared secrets)
macro_rules! impl_ecdh_typed_owned {
($ty:ty => $randomness_len:expr, $secret_len:expr, $public_len:expr) => {
impl $crate::ecdh::typed_owned::EcdhTypes for $ty {
type Secret = [$crate::libcrux_secrets::U8; $secret_len];
type Public = [u8; $public_len];
type Randomness = [$crate::libcrux_secrets::U8; $randomness_len];
type Derived = [$crate::libcrux_secrets::U8; $public_len];
}

impl $crate::ecdh::typed_owned::Ecdh for $ty {
fn generate_secret(
rand: &Self::Randomness,
) -> Result<Self::Secret, $crate::ecdh::owned::GenerateSecretError> {
<$ty as $crate::ecdh::owned::EcdhOwned<$randomness_len,
$secret_len,
$public_len
>>::generate_secret(rand)
}

fn secret_to_public(
secret: &Self::Secret,
) -> Result<Self::Public, $crate::ecdh::owned::SecretToPublicError> {
<$ty as $crate::ecdh::owned::EcdhOwned<$randomness_len,
$secret_len,
$public_len
>>::secret_to_public(secret)
}

fn derive_ecdh(
public: &Self::Public,
secret: &Self::Secret,
) -> Result<Self::Derived, $crate::ecdh::owned::DeriveError> {
<$ty as $crate::ecdh::owned::EcdhOwned<$randomness_len,
$secret_len,
$public_len
>>::derive_ecdh(public, secret)
}

fn validate_secret(
secret: &Self::Secret,
) -> Result<(), $crate::ecdh::owned::ValidateSecretError> {
<$ty as $crate::ecdh::owned::EcdhOwned<$randomness_len,
$secret_len,
$public_len
>>::validate_secret(secret)
}
}
};
}

pub use impl_ecdh_typed_owned;