libcrux_ecdh/hacl/
p256.rs

1use libcrux_p256::{
2    compressed_to_raw, dh_initiator, dh_responder, uncompressed_to_raw, validate_private_key,
3    validate_public_key,
4};
5
6use crate::p256_internal::PrivateKey;
7
8#[derive(Debug, PartialEq, Eq, Clone, Copy)]
9pub enum Error {
10    InvalidInput,
11    InvalidScalar,
12    InvalidPoint,
13    NoCompressedPoint,
14    NoUnCompressedPoint,
15}
16
17/// Parse an uncompressed P256 point and return the 64 byte array with the
18/// concatenation of X||Y
19pub fn uncompressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> {
20    let mut concat_point = [0u8; 64];
21    if point.len() >= 65 {
22        let ok = uncompressed_to_raw(point, &mut concat_point);
23        if ok {
24            Ok(concat_point)
25        } else {
26            Err(Error::InvalidInput)
27        }
28    } else {
29        Err(Error::NoCompressedPoint)
30    }
31}
32
33/// Parse an compressed P256 point and return the 64 byte array with the
34/// concatenation of `X` and `Y`.
35pub fn compressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> {
36    let mut concat_point = [0u8; 64];
37    if point.len() >= 33 {
38        let ok = compressed_to_raw(point, &mut concat_point);
39        if ok {
40            Ok(concat_point)
41        } else {
42            Err(Error::InvalidInput)
43        }
44    } else {
45        Err(Error::NoUnCompressedPoint)
46    }
47}
48
49/// Validate a P256 point, where `point` is a 64 byte array with the
50/// concatenation of `X` and `Y`.
51///
52/// Returns [`Error::InvalidPoint`] if the `point` is not valid.
53pub fn validate_point(point: impl AsRef<[u8; 64]>) -> Result<(), Error> {
54    if validate_public_key(point.as_ref()) {
55        Ok(())
56    } else {
57        Err(Error::InvalidPoint)
58    }
59}
60
61/// Validate a P256 secret key (scalar).
62///
63/// Returns [`Error::InvalidScalar`] if the `scalar` is not valid.
64pub fn validate_scalar(scalar: &impl AsRef<[u8; 32]>) -> Result<(), Error> {
65    validate_scalar_(scalar.as_ref())
66}
67
68/// Validate a P256 secret key (scalar).
69///
70/// Returns [`Error::InvalidScalar`] if the `scalar` is not valid.
71pub fn validate_scalar_(scalar: &[u8; 32]) -> Result<(), Error> {
72    if scalar.as_ref().iter().all(|b| *b == 0) {
73        return Err(Error::InvalidScalar);
74    }
75
76    // Ensure that the key is in range  [1, p-1]
77    if validate_private_key(scalar.as_ref()) {
78        Ok(())
79    } else {
80        Err(Error::InvalidScalar)
81    }
82}
83
84/// Validate a P256 secret key (scalar).
85pub fn validate_scalar_slice(scalar: &[u8]) -> Result<PrivateKey, Error> {
86    if scalar.is_empty() {
87        return Err(Error::InvalidScalar);
88    }
89
90    let mut private = [0u8; 32];
91    // Force the length of `sk` to 32 bytes.
92    let sk_len = if scalar.len() >= 32 { 32 } else { scalar.len() };
93    for i in 0..sk_len {
94        private[31 - i] = scalar[scalar.len() - 1 - i];
95    }
96
97    validate_scalar_(&private).map(|_| PrivateKey(private))
98}
99
100/// Compute the ECDH with the `private_key` and `public_key`.
101///
102/// Returns the 64 bytes shared key.
103pub fn ecdh(
104    private_key: impl AsRef<[u8; 32]>,
105    public_key: impl AsRef<[u8; 64]>,
106) -> Result<[u8; 64], Error> {
107    let mut shared = [0u8; 64];
108    let ok = dh_responder(&mut shared, public_key.as_ref(), private_key.as_ref());
109    if !ok {
110        Err(Error::InvalidInput)
111    } else {
112        Ok(shared)
113    }
114}
115
116/// Compute the public key for the provided `private_key`.
117///
118/// Returns the 64 bytes public key.
119pub fn secret_to_public(s: impl AsRef<[u8; 32]>) -> Result<[u8; 64], Error> {
120    validate_scalar(&s)?;
121
122    let mut out = [0u8; 64];
123    if dh_initiator(&mut out, s.as_ref()) {
124        Ok(out)
125    } else {
126        Err(Error::InvalidScalar)
127    }
128}