libcrux_ecdh/
x25519.rs

1// XXX: This could be simplified with the pure Rust version now.
2
3use alloc::format;
4use rand::{CryptoRng, Rng, TryRngCore};
5
6use super::Error;
7
8pub struct PrivateKey(pub [u8; 32]);
9
10#[derive(Debug)]
11pub struct PublicKey(pub [u8; 32]);
12
13/// Output of a scalar multiplication between a public key and a secret key.
14///
15/// This value is NOT (!) safe for use as a key and needs to be processed in a round of key
16/// derivation, to ensure both that the output is uniformly random and that unkown key share
17/// attacks can not happen.
18pub struct SharedSecret(pub [u8; 32]);
19
20impl From<&[u8; 32]> for PublicKey {
21    fn from(value: &[u8; 32]) -> Self {
22        Self(*value)
23    }
24}
25
26impl TryFrom<&[u8]> for PublicKey {
27    type Error = Error;
28
29    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
30        Ok(Self(value.try_into().map_err(|_| Error::InvalidPoint)?))
31    }
32}
33
34impl From<&[u8; 32]> for PrivateKey {
35    fn from(value: &[u8; 32]) -> Self {
36        Self(*value)
37    }
38}
39
40impl TryFrom<&[u8]> for PrivateKey {
41    type Error = Error;
42
43    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
44        Ok(Self(value.try_into().map_err(|_| Error::InvalidScalar)?))
45    }
46}
47
48impl From<&[u8; 32]> for SharedSecret {
49    fn from(value: &[u8; 32]) -> Self {
50        Self(*value)
51    }
52}
53
54impl TryFrom<&[u8]> for SharedSecret {
55    type Error = Error;
56
57    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
58        Ok(Self(value.try_into().map_err(|_| Error::InvalidScalar)?))
59    }
60}
61
62impl AsRef<[u8]> for PrivateKey {
63    fn as_ref(&self) -> &[u8] {
64        &self.0
65    }
66}
67
68impl AsRef<[u8]> for PublicKey {
69    fn as_ref(&self) -> &[u8] {
70        &self.0
71    }
72}
73
74impl AsRef<[u8]> for SharedSecret {
75    fn as_ref(&self) -> &[u8] {
76        &self.0
77    }
78}
79
80impl AsRef<[u8; 32]> for PrivateKey {
81    fn as_ref(&self) -> &[u8; 32] {
82        &self.0
83    }
84}
85
86impl AsRef<[u8; 32]> for PublicKey {
87    fn as_ref(&self) -> &[u8; 32] {
88        &self.0
89    }
90}
91
92impl AsRef<[u8; 32]> for SharedSecret {
93    fn as_ref(&self) -> &[u8; 32] {
94        &self.0
95    }
96}
97
98pub fn derive(p: &PublicKey, s: &PrivateKey) -> Result<SharedSecret, Error> {
99    // Use the portable HACL implementation.
100    use crate::hacl::curve25519;
101
102    curve25519::ecdh(s, p)
103        .map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))
104        .map(SharedSecret)
105}
106
107pub(crate) fn secret_to_public(s: &PrivateKey) -> Result<PublicKey, Error> {
108    // Use the portable HACL implementation.
109    use crate::hacl::curve25519;
110
111    Ok(PublicKey(curve25519::secret_to_public(s)))
112}
113
114/// Generate a new x25519 secret.
115pub fn generate_secret(rng: &mut (impl CryptoRng + Rng)) -> Result<PrivateKey, Error> {
116    const LIMIT: usize = 100;
117    for _ in 0..LIMIT {
118        let mut out = [0u8; 32];
119        rng.try_fill_bytes(&mut out)
120            .map_err(|_| Error::KeyGenError)?;
121
122        // We don't want a 0 key.
123        if out.iter().all(|&b| b == 0) {
124            continue;
125        }
126
127        // We clamp the key already to make sure it can't be misused.
128        out[0] &= 248u8;
129        out[31] &= 127u8;
130        out[31] |= 64u8;
131
132        return Ok(PrivateKey(out));
133    }
134
135    Err(Error::KeyGenError)
136}
137
138/// Generate a new P256 key pair
139pub fn key_gen(rng: &mut (impl CryptoRng + Rng)) -> Result<(PrivateKey, PublicKey), Error> {
140    let sk = generate_secret(rng)?;
141    let pk = secret_to_public(&sk)?;
142    Ok((sk, pk))
143}