libcrux_ecdh/
p256_internal.rs

1use alloc::format;
2use rand::{CryptoRng, Rng, TryRngCore};
3
4// P256 we only have in HACL
5use crate::hacl::p256;
6
7use super::Error;
8
9pub struct PrivateKey(pub [u8; 32]);
10
11#[derive(Debug)]
12pub struct PublicKey(pub [u8; 64]);
13
14/// Output of a scalar multiplication between a public key and a secret key.
15///
16/// This value is NOT (!) safe for use as a key and needs to be processed in a round of key
17/// derivation, to ensure both that the output is uniformly random and that unkown key share
18/// attacks can not happen.
19pub struct SharedSecret(pub [u8; 64]);
20
21impl From<&[u8; 64]> for PublicKey {
22    fn from(value: &[u8; 64]) -> Self {
23        Self(*value)
24    }
25}
26
27impl TryFrom<&[u8]> for PublicKey {
28    type Error = Error;
29
30    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
31        Ok(Self(value.try_into().map_err(|_| Error::InvalidPoint)?))
32    }
33}
34
35impl From<&[u8; 32]> for PrivateKey {
36    fn from(value: &[u8; 32]) -> Self {
37        Self(*value)
38    }
39}
40
41impl TryFrom<&[u8]> for PrivateKey {
42    type Error = Error;
43
44    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
45        Ok(Self(value.try_into().map_err(|_| Error::InvalidScalar)?))
46    }
47}
48
49impl From<&[u8; 64]> for SharedSecret {
50    fn from(value: &[u8; 64]) -> Self {
51        Self(*value)
52    }
53}
54
55impl TryFrom<&[u8]> for SharedSecret {
56    type Error = Error;
57
58    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
59        Ok(Self(value.try_into().map_err(|_| Error::InvalidScalar)?))
60    }
61}
62
63impl AsRef<[u8]> for PrivateKey {
64    fn as_ref(&self) -> &[u8] {
65        &self.0
66    }
67}
68
69impl AsRef<[u8]> for PublicKey {
70    fn as_ref(&self) -> &[u8] {
71        &self.0
72    }
73}
74
75impl AsRef<[u8]> for SharedSecret {
76    fn as_ref(&self) -> &[u8] {
77        &self.0
78    }
79}
80
81impl AsRef<[u8; 32]> for PrivateKey {
82    fn as_ref(&self) -> &[u8; 32] {
83        &self.0
84    }
85}
86
87impl AsRef<[u8; 64]> for PublicKey {
88    fn as_ref(&self) -> &[u8; 64] {
89        &self.0
90    }
91}
92
93impl AsRef<[u8; 64]> for SharedSecret {
94    fn as_ref(&self) -> &[u8; 64] {
95        &self.0
96    }
97}
98
99pub(super) fn derive(p: &PublicKey, s: &PrivateKey) -> Result<SharedSecret, Error> {
100    // We assume that the private key has been validated.
101    p256::ecdh(s, p)
102        .map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))
103        .map(SharedSecret)
104}
105
106pub(super) fn secret_to_public(s: &PrivateKey) -> Result<PublicKey, Error> {
107    p256::validate_scalar(s).map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))?;
108    p256::secret_to_public(s)
109        .map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))
110        .map(PublicKey)
111}
112
113pub fn validate_scalar(s: &PrivateKey) -> Result<(), Error> {
114    p256::validate_scalar(s).map_err(|e| e.into())
115}
116
117#[allow(unused)]
118pub fn validate_point(p: &PublicKey) -> Result<(), Error> {
119    p256::validate_point(p).map_err(|e| e.into())
120}
121
122pub(crate) fn prepare_public_key(public_key: &[u8]) -> Result<PublicKey, Error> {
123    if public_key.is_empty() {
124        return Err(Error::InvalidPoint);
125    }
126
127    // Parse the public key.
128    let pk = if let Ok(pk) = p256::uncompressed_to_coordinates(public_key) {
129        pk
130    } else {
131        // Might be uncompressed
132        if public_key.len() == 33 {
133            p256::compressed_to_coordinates(public_key).map_err(|_| Error::InvalidPoint)?
134        } else {
135            // Might be a simple concatenation
136            public_key.try_into().map_err(|_| Error::InvalidPoint)?
137        }
138    };
139    let pk = PublicKey(pk);
140
141    p256::validate_point(&pk)
142        .map(|()| pk)
143        .map_err(|_| Error::InvalidPoint)
144}
145
146/// Generate a new p256 secret (scalar)
147pub fn generate_secret(rng: &mut (impl CryptoRng + Rng)) -> Result<PrivateKey, Error> {
148    const LIMIT: usize = 100;
149    for _ in 0..LIMIT {
150        let mut out = [0u8; 32];
151        rng.try_fill_bytes(&mut out)
152            .map_err(|_| Error::KeyGenError)?;
153
154        let out = PrivateKey(out);
155        if validate_scalar(&out).is_ok() {
156            return Ok(out);
157        }
158    }
159    Err(Error::KeyGenError)
160}
161
162/// Generate a new P256 key pair
163pub fn key_gen(rng: &mut (impl CryptoRng + Rng)) -> Result<(PrivateKey, PublicKey), Error> {
164    let sk = generate_secret(rng)?;
165    let pk = secret_to_public(&sk)?;
166    Ok((sk, pk))
167}