Skip to main content

bssl_crypto/
ecdsa.rs

1// Copyright 2024 The BoringSSL Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Elliptic Curve Digital Signature Algorithm.
16//!
17//! The module implements ECDSA for the NIST curves P-256 and P-384.
18//!
19//! ```
20//! use bssl_crypto::{ecdsa, ec::P256};
21//!
22//! let key = ecdsa::PrivateKey::<P256>::generate();
23//! // Publish your public key.
24//! let public_key_bytes = key.to_der_subject_public_key_info();
25//!
26//! // Sign and publish some message.
27//! let signed_message = b"hello world";
28//! let mut sig = key.sign(signed_message);
29//!
30//! // Anyone with the public key can verify it.
31//! let public_key = ecdsa::PublicKey::<P256>::from_der_subject_public_key_info(
32//!     public_key_bytes.as_ref()).unwrap();
33//! assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
34//! ```
35
36use crate::{
37    ec::{self, Group},
38    with_output_vec, Buffer, FfiSlice, InvalidSignatureError,
39};
40use alloc::vec::Vec;
41use core::marker::PhantomData;
42
43/// An ECDSA public key over the given curve.
44pub struct PublicKey<C: ec::Curve> {
45    point: ec::Point,
46    marker: PhantomData<C>,
47}
48
49impl<C: ec::Curve> PublicKey<C> {
50    /// Parse a public key in uncompressed X9.62 format. (This is the common
51    /// format for elliptic curve points beginning with an 0x04 byte.)
52    pub fn from_x962_uncompressed(x962: &[u8]) -> Option<Self> {
53        let point = ec::Point::from_x962_uncompressed(C::group(), x962)?;
54        Some(Self {
55            point,
56            marker: PhantomData,
57        })
58    }
59
60    /// Serialize this key as uncompressed X9.62 format.
61    pub fn to_x962_uncompressed(&self) -> Buffer {
62        self.point.to_x962_uncompressed()
63    }
64
65    /// Parse a public key in compressed X9.62 point format.
66    pub fn from_x962_compressed(x962: &[u8]) -> Option<Self> {
67        let point = ec::Point::from_x962_compressed(C::group(), x962)?;
68        Some(Self {
69            point,
70            marker: PhantomData,
71        })
72    }
73
74    /// Serialize this key as compressed X9.62 format.
75    ///
76    /// WARNING: compressed form is rarely used and is not as well supported as
77    /// the uncompressed form.
78    pub fn to_x962_compressed(&self) -> Buffer {
79        self.point.to_x962_compressed()
80    }
81
82    /// Parse a public key in SubjectPublicKeyInfo format.
83    /// (This is found in, e.g., X.509 certificates.)
84    pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option<Self> {
85        let point = ec::Point::from_der_subject_public_key_info(C::group(), spki)?;
86        Some(Self {
87            point,
88            marker: PhantomData,
89        })
90    }
91
92    /// Serialize this key in SubjectPublicKeyInfo format.
93    pub fn to_der_subject_public_key_info(&self) -> Buffer {
94        self.point.to_der_subject_public_key_info()
95    }
96
97    /// Verify `signature` as a valid ASN.1-based signature of a digest of
98    /// `signed_msg` with this public key. SHA-256 will be used to produce the
99    /// digest if the curve of this public key is P-256. SHA-384 will be used to
100    /// produce the digest if the curve of this public key is P-384.
101    pub fn verify(&self, signed_msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> {
102        let digest = C::hash(signed_msg);
103        let result = self.point.with_point_as_ec_key(|ec_key| unsafe {
104            // Safety: `ec_key` is valid per `with_point_as_ec_key`.
105            bssl_sys::ECDSA_verify(
106                /*type=*/ 0,
107                digest.as_slice().as_ffi_ptr(),
108                digest.len(),
109                signature.as_ffi_ptr(),
110                signature.len(),
111                ec_key,
112            )
113        });
114        if result == 1 {
115            Ok(())
116        } else {
117            Err(InvalidSignatureError)
118        }
119    }
120
121    /// Verify `signature` as a valid P1363-based signature of a digest of
122    /// `signed_msg` with this public key. SHA-256 will be used to produce the
123    /// digest if the curve of this public key is P-256. SHA-384 will be used to
124    /// produce the digest if the curve of this public key is P-384.
125    pub fn verify_p1363(
126        &self,
127        signed_msg: &[u8],
128        signature: &[u8],
129    ) -> Result<(), InvalidSignatureError> {
130        let digest = C::hash(signed_msg);
131        let result = self.point.with_point_as_ec_key(|ec_key| unsafe {
132            // Safety: `ec_key` is valid per `with_point_as_ec_key`.
133            bssl_sys::ECDSA_verify_p1363(
134                digest.as_slice().as_ffi_ptr(),
135                digest.len(),
136                signature.as_ffi_ptr(),
137                signature.len(),
138                ec_key,
139            )
140        });
141        if result == 1 {
142            Ok(())
143        } else {
144            Err(InvalidSignatureError)
145        }
146    }
147}
148
149/// An ECDSA private key over the given curve.
150pub struct PrivateKey<C: ec::Curve> {
151    key: ec::Key,
152    marker: PhantomData<C>,
153}
154
155impl<C: ec::Curve> Clone for PrivateKey<C> {
156    fn clone(&self) -> Self {
157        Self {
158            key: self.key.clone(),
159            marker: PhantomData,
160        }
161    }
162}
163
164/// Parsed `ECPrivateKey` dispatched into the corresponding curve types.
165pub enum ParsedPrivateKey {
166    /// A P-256 private key.
167    P256(PrivateKey<ec::P256>),
168    /// A P-384 private key.
169    P384(PrivateKey<ec::P384>),
170}
171
172impl ParsedPrivateKey {
173    /// Parses an ECPrivateKey structure from a DER encoded structure per [RFC 5915],
174    /// whose curve is specified by the `ECParameters`.
175    ///
176    /// Unless the curve group is one of the variants of [`Group`], this method returns [`None`].
177    ///
178    /// [RFC 5915]: <https://datatracker.ietf.org/doc/html/rfc5915>
179    pub fn from_der(der: &[u8]) -> Option<Self> {
180        let key = ec::Key::from_der_ec_private_key_with_curve_names(der)?;
181        match key.get_group()? {
182            Group::P256 => Some(ParsedPrivateKey::P256(PrivateKey {
183                key,
184                marker: PhantomData,
185            })),
186            Group::P384 => Some(ParsedPrivateKey::P384(PrivateKey {
187                key,
188                marker: PhantomData,
189            })),
190        }
191    }
192}
193
194impl<C: ec::Curve> PrivateKey<C> {
195    /// Generate a random private key.
196    pub fn generate() -> Self {
197        Self {
198            key: ec::Key::generate(C::group()),
199            marker: PhantomData,
200        }
201    }
202
203    /// Parse a `PrivateKey` from a zero-padded, big-endian representation of the secret scalar.
204    pub fn from_big_endian(scalar: &[u8]) -> Option<Self> {
205        let key = ec::Key::from_big_endian(C::group(), scalar)?;
206        Some(Self {
207            key,
208            marker: PhantomData,
209        })
210    }
211
212    /// Return the private key as zero-padded, big-endian bytes.
213    pub fn to_big_endian(&self) -> Buffer {
214        self.key.to_big_endian()
215    }
216
217    /// Parse an ECPrivateKey structure (from RFC 5915). The key must be on the
218    /// specified curve.
219    pub fn from_der_ec_private_key(der: &[u8]) -> Option<Self> {
220        let key = ec::Key::from_der_ec_private_key(C::group(), der)?;
221        Some(Self {
222            key,
223            marker: PhantomData,
224        })
225    }
226
227    /// Serialize this private key as an ECPrivateKey structure (from RFC 5915).
228    pub fn to_der_ec_private_key(&self) -> Buffer {
229        self.key.to_der_ec_private_key()
230    }
231
232    /// Parse a PrivateKeyInfo structure (from RFC 5208), commonly called
233    /// "PKCS#8 format". The key must be on the specified curve.
234    pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
235        let key = ec::Key::from_der_private_key_info(C::group(), der)?;
236        Some(Self {
237            key,
238            marker: PhantomData,
239        })
240    }
241
242    // Caller must make sure that the group of the key matches `C`,
243    // or else it panics.
244    pub(crate) fn from_ec_key(key: ec::Key) -> Self {
245        assert_eq!(key.get_group(), Some(C::group()));
246        Self {
247            key,
248            marker: PhantomData,
249        }
250    }
251
252    /// Serialize this private key as a PrivateKeyInfo structure (from RFC 5208),
253    /// commonly called "PKCS#8 format".
254    pub fn to_der_private_key_info(&self) -> Buffer {
255        self.key.to_der_private_key_info()
256    }
257
258    /// Serialize the _public_ part of this key in uncompressed X9.62 format.
259    pub fn to_x962_uncompressed(&self) -> Buffer {
260        self.key.to_x962_uncompressed()
261    }
262
263    /// Serialize the _public_ part of this key in compressed X9.62 format.
264    ///
265    /// WARNING: compressed form is rarely used and is not as well supported as
266    /// the uncompressed form.
267    pub fn to_x962_compressed(&self) -> Buffer {
268        self.key.to_x962_compressed()
269    }
270
271    /// Serialize this key in SubjectPublicKeyInfo format.
272    pub fn to_der_subject_public_key_info(&self) -> Buffer {
273        self.key.to_der_subject_public_key_info()
274    }
275
276    /// Return the public key corresponding to this private key.
277    pub fn to_public_key(&self) -> PublicKey<C> {
278        PublicKey {
279            point: self.key.to_point(),
280            marker: PhantomData,
281        }
282    }
283
284    /// Sign a digest of `to_be_signed` using this key and return the
285    /// ASN.1-based signature. SHA-256 will be used to produce the digest if the
286    /// curve of this public key is P-256. SHA-384 will be used to produce the
287    /// digest if the curve of this public key is P-384.
288    pub fn sign(&self, to_be_signed: &[u8]) -> Vec<u8> {
289        // Safety: `self.key` is valid by construction.
290        let max_size = unsafe { bssl_sys::ECDSA_size(self.key.as_ffi_ptr()) };
291        // No curve can be empty.
292        assert_ne!(max_size, 0);
293
294        let digest = C::hash(to_be_signed);
295
296        unsafe {
297            with_output_vec(max_size, |out_buf| {
298                let mut out_len: core::ffi::c_uint = 0;
299                // Safety: `out_buf` points to at least `max_size` bytes,
300                // as required.
301                let result = {
302                    bssl_sys::ECDSA_sign(
303                        /*type=*/ 0,
304                        digest.as_slice().as_ffi_ptr(),
305                        digest.len(),
306                        out_buf,
307                        &mut out_len,
308                        self.key.as_ffi_ptr(),
309                    )
310                };
311                // Signing should never fail unless we're out of memory,
312                // which this crate doesn't handle.
313                assert_eq!(result, 1);
314                let out_len = out_len as usize;
315                assert!(out_len <= max_size);
316                // Safety: `out_len` bytes have been written.
317                out_len
318            })
319        }
320    }
321
322    /// Sign a digest of `to_be_signed` using this key and return the
323    /// P1363-based signature. SHA-256 will be used to produce the digest if
324    /// the curve of this public key is P-256. SHA-384 will be used to produce
325    /// the digest if the curve of this public key is P-384.
326    pub fn sign_p1363(&self, to_be_signed: &[u8]) -> Vec<u8> {
327        // Safety: `self.key` is valid by construction.
328        let max_size = unsafe { bssl_sys::ECDSA_size_p1363(self.key.as_ffi_ptr()) };
329        // No curve can be empty.
330        assert_ne!(max_size, 0);
331
332        let digest = C::hash(to_be_signed);
333
334        unsafe {
335            with_output_vec(max_size, |out_buf| {
336                let mut out_len = 0usize;
337                // Safety: `out_buf` points to at least `size` bytes, as
338                // required.
339                let result = {
340                    bssl_sys::ECDSA_sign_p1363(
341                        digest.as_slice().as_ffi_ptr(),
342                        digest.len(),
343                        out_buf,
344                        &mut out_len,
345                        max_size,
346                        self.key.as_ffi_ptr(),
347                    )
348                };
349                // Signing should never fail unless we're out of memory,
350                // which this crate doesn't handle.
351                assert_eq!(result, 1);
352                assert!(out_len <= max_size);
353                // Safety: `out_len` bytes have been written.
354                out_len
355            })
356        }
357    }
358}
359
360#[cfg(test)]
361mod test {
362    use super::*;
363    use crate::ec::{P256, P384};
364
365    fn check_curve<C: ec::Curve>() {
366        let signed_message = b"hello world";
367        let key = PrivateKey::<C>::generate();
368        let mut sig = key.sign(signed_message);
369        let mut sig_p1363 = key.sign_p1363(signed_message);
370
371        let public_key = PublicKey::<C>::from_der_subject_public_key_info(
372            key.to_der_subject_public_key_info().as_ref(),
373        )
374        .unwrap();
375        assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
376        assert!(public_key
377            .verify_p1363(signed_message, sig_p1363.as_slice())
378            .is_ok());
379
380        sig[10] ^= 1;
381        assert!(public_key.verify(signed_message, sig.as_slice()).is_err());
382        sig_p1363[10] ^= 1;
383        assert!(public_key
384            .verify_p1363(signed_message, sig_p1363.as_slice())
385            .is_err());
386    }
387
388    fn check_compressed<C: ec::Curve>() {
389        let signed_message = b"hello world";
390        let key = PrivateKey::<C>::generate();
391        let mut sig = key.sign(signed_message);
392        let mut sig_p1363 = key.sign_p1363(signed_message);
393
394        let public_key =
395            PublicKey::<C>::from_x962_compressed(key.to_x962_compressed().as_ref()).unwrap();
396        assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
397        assert!(public_key
398            .verify_p1363(signed_message, sig_p1363.as_slice())
399            .is_ok());
400
401        sig[10] ^= 1;
402        assert!(public_key.verify(signed_message, sig.as_slice()).is_err());
403        sig_p1363[10] ^= 1;
404        assert!(public_key
405            .verify_p1363(signed_message, sig_p1363.as_slice())
406            .is_err());
407    }
408
409    fn check_parsing<C: ec::Curve>() {
410        let key = PrivateKey::<C>::generate();
411        let der = key.to_der_ec_private_key();
412        let parsed = ParsedPrivateKey::from_der(der.as_ref()).unwrap();
413        match parsed {
414            ParsedPrivateKey::P256(_) => assert!(matches!(C::group(), Group::P256)),
415            ParsedPrivateKey::P384(_) => assert!(matches!(C::group(), Group::P384)),
416        }
417    }
418
419    #[test]
420    fn p256() {
421        check_curve::<P256>();
422        check_compressed::<P256>();
423        check_parsing::<P256>();
424    }
425
426    #[test]
427    fn p384() {
428        check_curve::<P384>();
429        check_compressed::<P384>();
430        check_parsing::<P384>();
431    }
432}