libcrux_ecdsa/
p256.rs

1//! ECDSA on P-256
2
3use libcrux_p256::{
4    compressed_to_raw, ecdsa_sign_p256_sha2, ecdsa_sign_p256_sha384, ecdsa_sign_p256_sha512,
5    ecdsa_verif_p256_sha2, ecdsa_verif_p256_sha384, ecdsa_verif_p256_sha512, uncompressed_to_raw,
6    validate_private_key, validate_public_key,
7};
8
9use crate::DigestAlgorithm;
10
11use ::rand::TryRngCore;
12
13use super::Error;
14
15/// A P-256 Signature
16#[derive(Clone, Default)]
17pub struct Signature {
18    r: [u8; 32],
19    s: [u8; 32],
20}
21
22/// An ECDSA P-256 nonce
23pub struct Nonce([u8; 32]);
24
25/// An ECDSA P-256 private key
26pub struct PrivateKey([u8; 32]);
27
28/// An ECDSA P-256 public key
29#[derive(Debug)]
30pub struct PublicKey(pub [u8; 64]);
31
32mod conversions {
33    use super::*;
34
35    impl Signature {
36        /// Generate a signature from the raw values r and s.
37        pub fn from_raw(r: [u8; 32], s: [u8; 32]) -> Self {
38            Self { r, s }
39        }
40
41        /// Generate a signature from the raw values r || s.
42        pub fn from_bytes(signature_bytes: [u8; 64]) -> Self {
43            Self {
44                r: signature_bytes[0..32].try_into().unwrap(),
45                s: signature_bytes[32..].try_into().unwrap(),
46            }
47        }
48
49        /// Get the signature as the two raw 32 bytes `(r, s)`.
50        pub fn as_bytes(&self) -> (&[u8; 32], &[u8; 32]) {
51            (&self.r, &self.s)
52        }
53    }
54
55    impl TryFrom<&[u8; 32]> for PrivateKey {
56        type Error = Error;
57
58        fn try_from(value: &[u8; 32]) -> Result<Self, Self::Error> {
59            validate_private_key_slice(value)
60        }
61    }
62
63    impl TryFrom<&[u8]> for PrivateKey {
64        type Error = Error;
65
66        fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
67            validate_private_key_slice(value)
68        }
69    }
70
71    impl AsRef<[u8]> for PrivateKey {
72        fn as_ref(&self) -> &[u8] {
73            &self.0
74        }
75    }
76
77    impl AsRef<[u8; 32]> for PrivateKey {
78        fn as_ref(&self) -> &[u8; 32] {
79            &self.0
80        }
81    }
82
83    impl TryFrom<&[u8; 64]> for PublicKey {
84        type Error = Error;
85
86        fn try_from(value: &[u8; 64]) -> Result<Self, Self::Error> {
87            validate_pk(value)
88        }
89    }
90
91    impl TryFrom<&[u8]> for PublicKey {
92        type Error = Error;
93
94        fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
95            validate_pk(value)
96        }
97    }
98
99    impl AsRef<[u8]> for PublicKey {
100        fn as_ref(&self) -> &[u8] {
101            &self.0
102        }
103    }
104
105    impl AsRef<[u8; 64]> for PublicKey {
106        fn as_ref(&self) -> &[u8; 64] {
107            &self.0
108        }
109    }
110}
111
112/// Parse an uncompressed P256 point and return the 64 byte array with the
113/// concatenation of X||Y
114pub fn uncompressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> {
115    let mut concat_point = [0u8; 64];
116    if point.len() >= 65 {
117        if uncompressed_to_raw(point, &mut concat_point) {
118            Ok(concat_point)
119        } else {
120            Err(Error::InvalidInput)
121        }
122    } else {
123        Err(Error::NoCompressedPoint)
124    }
125}
126
127/// Parse an compressed P256 point and return the 64 byte array with the
128/// concatenation of `X` and `Y`.
129pub fn compressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> {
130    let mut concat_point = [0u8; 64];
131    if point.len() >= 33 {
132        if compressed_to_raw(point, &mut concat_point) {
133            Ok(concat_point)
134        } else {
135            Err(Error::InvalidInput)
136        }
137    } else {
138        Err(Error::NoUnCompressedPoint)
139    }
140}
141
142/// Validate a P256 point, where `point` is a 64 byte array with the
143/// concatenation of `X` and `Y`.
144///
145/// Returns [`Error::InvalidPoint`] if the `point` is not valid.
146pub fn validate_point(point: &[u8]) -> Result<(), Error> {
147    if validate_public_key(point) {
148        Ok(())
149    } else {
150        Err(Error::InvalidPoint)
151    }
152}
153
154/// Validate a P256 secret key (scalar).
155///
156/// Returns [`Error::InvalidScalar`] if the `scalar` is not valid.
157pub fn validate_scalar(scalar: &impl AsRef<[u8; 32]>) -> Result<(), Error> {
158    validate_scalar_(scalar.as_ref())
159}
160
161/// Validate a P256 secret key (scalar).
162///
163/// Returns [`Error::InvalidScalar`] if the `scalar` is not valid.
164fn validate_scalar_(scalar: &[u8; 32]) -> Result<(), Error> {
165    if scalar.as_ref().iter().all(|b| *b == 0) {
166        return Err(Error::InvalidScalar);
167    }
168
169    // Ensure that the key is in range  [1, p-1]
170    if validate_private_key(scalar.as_ref()) {
171        Ok(())
172    } else {
173        Err(Error::InvalidScalar)
174    }
175}
176
177/// Validate a P256 secret key or nonce (scalar).
178fn validate_scalar_slice(scalar: &[u8]) -> Result<[u8; 32], Error> {
179    if scalar.is_empty() {
180        return Err(Error::InvalidScalar);
181    }
182
183    let mut private = [0u8; 32];
184    // Force the length of `sk` to 32 bytes.
185    let sk_len = if scalar.len() >= 32 { 32 } else { scalar.len() };
186    for i in 0..sk_len {
187        private[31 - i] = scalar[scalar.len() - 1 - i];
188    }
189
190    validate_scalar_(&private).map(|_| private)
191}
192
193fn validate_private_key_slice(scalar: &[u8]) -> Result<PrivateKey, Error> {
194    validate_scalar_slice(scalar).map(|a| PrivateKey(a))
195}
196
197/// Prepare the nonce for EcDSA and validate the key
198#[cfg(feature = "rand")]
199pub mod rand {
200    use crate::RAND_LIMIT;
201
202    use super::*;
203    use ::rand::{CryptoRng, RngCore};
204
205    /// Generate a random scalar for ECDSA.
206    ///
207    /// This can be a raw nonce or a private key.
208    ///
209    /// Use [`Nonce::random`] or [`PrivateKey::random`] to generate a nonce or
210    /// a private key instead.
211    pub fn random_scalar(rng: &mut (impl CryptoRng + RngCore)) -> Result<[u8; 32], Error> {
212        let mut value = [0u8; 32];
213        for _ in 0..RAND_LIMIT {
214            rng.try_fill_bytes(&mut value)
215                .map_err(|_| Error::RandError)?;
216
217            // Make sure it's a valid nonce.
218            if validate_scalar_slice(&value).is_ok() {
219                return Ok(value);
220            }
221        }
222        Err(Error::RandError)
223    }
224
225    impl Nonce {
226        /// Generate a random nonce for ECDSA.
227        pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Result<Self, Error> {
228            random_scalar(rng).map(|s| Self(s))
229        }
230    }
231
232    impl PrivateKey {
233        /// Generate a random [`PrivateKey`] for ECDSA.
234        pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Result<Self, Error> {
235            random_scalar(rng).map(|s| Self(s))
236        }
237    }
238
239    /// Sign the `payload` with the `private_key`.
240    pub fn sign(
241        hash: DigestAlgorithm,
242        payload: &[u8],
243        private_key: &PrivateKey,
244        rng: &mut (impl CryptoRng + RngCore),
245    ) -> Result<Signature, Error> {
246        let nonce = Nonce(random_scalar(rng)?);
247
248        super::_sign(hash, payload, private_key, &nonce)
249    }
250}
251
252/// Sign the `payload` with the `private_key` and `nonce`.
253///
254/// Returns an error if the `nonce` or `private_key` are invalid.
255pub fn sign(
256    hash: DigestAlgorithm,
257    payload: &[u8],
258    private_key: &PrivateKey,
259    nonce: &Nonce,
260) -> Result<Signature, Error> {
261    _sign(hash, payload, private_key, nonce)
262}
263
264/// Internal sign
265fn _sign(
266    hash: DigestAlgorithm,
267    payload: &[u8],
268    private_key: &PrivateKey,
269    nonce: &Nonce,
270) -> Result<Signature, Error> {
271    let mut signature = [0u8; 64];
272    let len = u32_len(payload)?;
273
274    let success = match hash {
275        DigestAlgorithm::Sha256 => {
276            ecdsa_sign_p256_sha2(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
277        }
278        DigestAlgorithm::Sha384 => {
279            ecdsa_sign_p256_sha384(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
280        }
281        DigestAlgorithm::Sha512 => {
282            ecdsa_sign_p256_sha512(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
283        }
284        libcrux_sha2::Algorithm::Sha224 => return Err(Error::UnsupportedHash),
285    };
286
287    if !success {
288        return Err(Error::SigningError);
289    }
290
291    Ok(Signature {
292        r: signature[..32]
293            .try_into()
294            .map_err(|_| Error::SigningError)?,
295        s: signature[32..]
296            .try_into()
297            .map_err(|_| Error::SigningError)?,
298    })
299}
300
301fn u32_len(bytes: &[u8]) -> Result<u32, Error> {
302    if bytes.len() > u32::MAX as usize {
303        return Err(Error::InvalidInput);
304    } else {
305        Ok(bytes.len() as u32)
306    }
307}
308
309/// Prepare the public key for EcDSA
310fn validate_pk(public_key: &[u8]) -> Result<PublicKey, Error> {
311    if public_key.is_empty() {
312        return Err(Error::SigningError);
313    }
314
315    // Parse the public key.
316    let pk = if let Ok(pk) = uncompressed_to_coordinates(public_key) {
317        pk
318    } else {
319        // Might be uncompressed
320        if let Ok(pk) = compressed_to_coordinates(public_key) {
321            pk
322        } else {
323            // Might be a simple concatenation
324            public_key.try_into().map_err(|_| Error::InvalidSignature)?
325        }
326    };
327
328    let pk = PublicKey(pk);
329    validate_point(&pk.0).map(|_| pk)
330}
331
332/// Verify the `payload` and `signature` with the `public_key`.
333///
334/// Return `()` or [`Error::InvalidSignature`].
335pub fn verify(
336    hash: DigestAlgorithm,
337    payload: &[u8],
338    signature: &Signature,
339    public_key: &PublicKey,
340) -> Result<(), Error> {
341    let len = u32_len(payload)?;
342
343    let success = match hash {
344        libcrux_sha2::Algorithm::Sha256 => {
345            ecdsa_verif_p256_sha2(len, payload, &public_key.0, &signature.r, &signature.s)
346        }
347        libcrux_sha2::Algorithm::Sha384 => {
348            ecdsa_verif_p256_sha384(len, payload, &public_key.0, &signature.r, &signature.s)
349        }
350        libcrux_sha2::Algorithm::Sha512 => {
351            ecdsa_verif_p256_sha512(len, payload, &public_key.0, &signature.r, &signature.s)
352        }
353        libcrux_sha2::Algorithm::Sha224 => return Err(Error::UnsupportedHash),
354    };
355
356    if success {
357        Ok(())
358    } else {
359        Err(Error::InvalidSignature)
360    }
361}