libcrux_ecdh/
ecdh.rs

1//! # ECDH
2//!
3//! ## x25519
4//! For x25519 the portable HACL implementation is used.
5//!
6//! ## P256
7//! For P256 the portable HACL implementation is used.
8#![no_std]
9
10extern crate alloc;
11
12use alloc::{string::String, vec::Vec};
13
14mod hacl;
15
16#[derive(Debug, PartialEq, Eq)]
17pub enum LowLevelError {
18    Jasmin(String),
19    Hacl(hacl::Error),
20}
21
22#[derive(Debug, PartialEq, Eq)]
23pub enum Error {
24    InvalidPoint,
25    InvalidScalar,
26    UnknownAlgorithm,
27    KeyGenError,
28    Custom(String),
29    Wrap(LowLevelError),
30}
31
32impl From<hacl::p256::Error> for Error {
33    fn from(value: hacl::p256::Error) -> Self {
34        Error::Wrap(LowLevelError::Hacl(hacl::Error::P256(value)))
35    }
36}
37
38/// ECDH algorithm.
39#[derive(Debug, PartialEq, Eq, Clone, Copy)]
40pub enum Algorithm {
41    X25519,
42    X448,
43    P256,
44    P384,
45    P521,
46}
47
48/// The internal x25519 module
49pub(crate) mod x25519;
50pub use x25519::{
51    derive as x25519_derive, generate_secret as x25519_generate_secret, key_gen as x25519_key_gen,
52    PrivateKey as X25519PrivateKey, PublicKey as X25519PublicKey,
53    SharedSecret as X25519SharedSecret,
54};
55
56pub mod curve25519 {
57    use super::hacl;
58    pub use hacl::curve25519::Error;
59}
60
61/// The internal p256 module
62pub(crate) mod p256_internal;
63
64pub mod p256 {
65    use super::hacl;
66    pub use hacl::p256::{
67        compressed_to_coordinates, uncompressed_to_coordinates, validate_point,
68        validate_scalar_slice, Error,
69    };
70}
71
72pub use p256_internal::{
73    generate_secret as p256_generate_secret, key_gen as p256_key_gen,
74    validate_scalar as p256_validate_scalar, PrivateKey as P256PrivateKey,
75    PublicKey as P256PublicKey, SharedSecret as P256SharedSecret,
76};
77
78/// Derive the ECDH shared secret.
79/// Returns `Ok(point * scalar)` on the provided curve [`Algorithm`] or an error.
80pub fn derive(
81    alg: Algorithm,
82    point: impl AsRef<[u8]>,
83    scalar: impl AsRef<[u8]>,
84) -> Result<Vec<u8>, Error> {
85    match alg {
86        Algorithm::X25519 => {
87            x25519::derive(&point.as_ref().try_into()?, &scalar.as_ref().try_into()?)
88                .map(|r| r.0.into())
89        }
90        Algorithm::P256 => {
91            let point = p256_internal::prepare_public_key(point.as_ref())?;
92            let scalar = hacl::p256::validate_scalar_slice(scalar.as_ref())
93                .map_err(|_| Error::InvalidScalar)?;
94
95            p256_internal::derive(&point, &scalar).map(|r| r.0.into())
96        }
97        _ => Err(Error::UnknownAlgorithm),
98    }
99}
100
101pub fn p256_derive(
102    point: &p256_internal::PublicKey,
103    scalar: &p256_internal::PrivateKey,
104) -> Result<p256_internal::SharedSecret, Error> {
105    p256_internal::validate_point(point)?;
106    p256_internal::validate_scalar(scalar)?;
107
108    p256_internal::derive(point, scalar)
109}
110
111/// Derive the public key for the provided secret key `scalar`.
112pub fn secret_to_public(alg: Algorithm, scalar: impl AsRef<[u8]>) -> Result<Vec<u8>, Error> {
113    match alg {
114        Algorithm::X25519 => {
115            x25519::secret_to_public(&scalar.as_ref().try_into()?).map(|r| r.0.into())
116        }
117        Algorithm::P256 => {
118            p256_internal::secret_to_public(&scalar.as_ref().try_into()?).map(|r| r.0.into())
119        }
120        _ => Err(Error::UnknownAlgorithm),
121    }
122}
123
124/// Validate a secret key.
125pub fn validate_scalar(alg: Algorithm, s: impl AsRef<[u8]>) -> Result<(), Error> {
126    match alg {
127        Algorithm::X25519 => {
128            if s.as_ref().iter().all(|&b| b == 0) {
129                Err(Error::InvalidScalar)
130            } else {
131                Ok(())
132            }
133        }
134        Algorithm::P256 => p256_internal::validate_scalar(&s.as_ref().try_into()?),
135        _ => Err(Error::UnknownAlgorithm),
136    }
137}
138
139use rand::{CryptoRng, Rng};
140
141/// Generate a new private key scalar.
142///
143/// The function returns the new scalar or an [`Error::KeyGenError`] if it was unable to
144/// generate a new key. If this happens, the provided `rng` is probably faulty.
145pub fn generate_secret(alg: Algorithm, rng: &mut (impl CryptoRng + Rng)) -> Result<Vec<u8>, Error> {
146    match alg {
147        Algorithm::X25519 => x25519::generate_secret(rng).map(|k| k.0.to_vec()),
148        Algorithm::P256 => p256_internal::generate_secret(rng).map(|k| k.0.to_vec()),
149        _ => Err(Error::UnknownAlgorithm),
150    }
151}
152
153/// Generate a fresh key pair.
154///
155/// The function returns the (secret key, public key) tuple, or an [`Error`].
156pub fn key_gen(
157    alg: Algorithm,
158    rng: &mut (impl CryptoRng + Rng),
159) -> Result<(Vec<u8>, Vec<u8>), Error> {
160    let sk = generate_secret(alg, rng)?;
161    let pk = secret_to_public(alg, &sk)?;
162    Ok((sk, pk))
163}