Skip to main content

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