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