Skip to main content

bssl_crypto/
ecdsa.rs

1/* Copyright 2024 The BoringSSL Authors
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 */
15
16//! Elliptic Curve Digital Signature Algorithm.
17//!
18//! The module implements ECDSA for the NIST curves P-256 and P-384.
19//!
20//! ```
21//! use bssl_crypto::{ecdsa, ec::P256};
22//!
23//! let key = ecdsa::PrivateKey::<P256>::generate();
24//! // Publish your public key.
25//! let public_key_bytes = key.to_der_subject_public_key_info();
26//!
27//! // Sign and publish some message.
28//! let signed_message = b"hello world";
29//! let mut sig = key.sign(signed_message);
30//!
31//! // Anyone with the public key can verify it.
32//! let public_key = ecdsa::PublicKey::<P256>::from_der_subject_public_key_info(
33//!     public_key_bytes.as_ref()).unwrap();
34//! assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
35//! ```
36
37use crate::{ec, sealed, with_output_vec, Buffer, FfiSlice, InvalidSignatureError};
38use alloc::vec::Vec;
39use core::marker::PhantomData;
40
41/// An ECDSA public key over the given curve.
42pub struct PublicKey<C: ec::Curve> {
43    point: ec::Point,
44    marker: PhantomData<C>,
45}
46
47impl<C: ec::Curve> PublicKey<C> {
48    /// Parse a public key in uncompressed X9.62 format. (This is the common
49    /// format for elliptic curve points beginning with an 0x04 byte.)
50    pub fn from_x962_uncompressed(x962: &[u8]) -> Option<Self> {
51        let point = ec::Point::from_x962_uncompressed(C::group(sealed::Sealed), x962)?;
52        Some(Self {
53            point,
54            marker: PhantomData,
55        })
56    }
57
58    /// Serialize this key as uncompressed X9.62 format.
59    pub fn to_x962_uncompressed(&self) -> Buffer {
60        self.point.to_x962_uncompressed()
61    }
62
63    /// Parse a public key in SubjectPublicKeyInfo format.
64    /// (This is found in, e.g., X.509 certificates.)
65    pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option<Self> {
66        let point = ec::Point::from_der_subject_public_key_info(C::group(sealed::Sealed), spki)?;
67        Some(Self {
68            point,
69            marker: PhantomData,
70        })
71    }
72
73    /// Serialize this key in SubjectPublicKeyInfo format.
74    pub fn to_der_subject_public_key_info(&self) -> Buffer {
75        self.point.to_der_subject_public_key_info()
76    }
77
78    /// Verify `signature` as a valid signature of a digest of `signed_msg`
79    /// with this public key. SHA-256 will be used to produce the digest if the
80    /// curve of this public key is P-256. SHA-384 will be used to produce the
81    /// digest if the curve of this public key is P-384.
82    pub fn verify(&self, signed_msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> {
83        let digest = C::hash(signed_msg);
84        let result = self.point.with_point_as_ec_key(|ec_key| unsafe {
85            // Safety: `ec_key` is valid per `with_point_as_ec_key`.
86            bssl_sys::ECDSA_verify(
87                /*type=*/ 0,
88                digest.as_slice().as_ffi_ptr(),
89                digest.len(),
90                signature.as_ffi_ptr(),
91                signature.len(),
92                ec_key,
93            )
94        });
95        if result == 1 {
96            Ok(())
97        } else {
98            Err(InvalidSignatureError)
99        }
100    }
101}
102
103/// An ECDH private key over the given curve.
104pub struct PrivateKey<C: ec::Curve> {
105    key: ec::Key,
106    marker: PhantomData<C>,
107}
108
109impl<C: ec::Curve> PrivateKey<C> {
110    /// Generate a random private key.
111    pub fn generate() -> Self {
112        Self {
113            key: ec::Key::generate(C::group(sealed::Sealed)),
114            marker: PhantomData,
115        }
116    }
117
118    /// Parse a `PrivateKey` from a zero-padded, big-endian representation of the secret scalar.
119    pub fn from_big_endian(scalar: &[u8]) -> Option<Self> {
120        let key = ec::Key::from_big_endian(C::group(sealed::Sealed), scalar)?;
121        Some(Self {
122            key,
123            marker: PhantomData,
124        })
125    }
126
127    /// Return the private key as zero-padded, big-endian bytes.
128    pub fn to_big_endian(&self) -> Buffer {
129        self.key.to_big_endian()
130    }
131
132    /// Parse an ECPrivateKey structure (from RFC 5915). The key must be on the
133    /// specified curve.
134    pub fn from_der_ec_private_key(der: &[u8]) -> Option<Self> {
135        let key = ec::Key::from_der_ec_private_key(C::group(sealed::Sealed), der)?;
136        Some(Self {
137            key,
138            marker: PhantomData,
139        })
140    }
141
142    /// Serialize this private key as an ECPrivateKey structure (from RFC 5915).
143    pub fn to_der_ec_private_key(&self) -> Buffer {
144        self.key.to_der_ec_private_key()
145    }
146
147    /// Parse a PrivateKeyInfo structure (from RFC 5208), commonly called
148    /// "PKCS#8 format". The key must be on the specified curve.
149    pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
150        let key = ec::Key::from_der_private_key_info(C::group(sealed::Sealed), der)?;
151        Some(Self {
152            key,
153            marker: PhantomData,
154        })
155    }
156
157    /// Serialize this private key as a PrivateKeyInfo structure (from RFC 5208),
158    /// commonly called "PKCS#8 format".
159    pub fn to_der_private_key_info(&self) -> Buffer {
160        self.key.to_der_private_key_info()
161    }
162
163    /// Serialize the _public_ part of this key in uncompressed X9.62 format.
164    pub fn to_x962_uncompressed(&self) -> Buffer {
165        self.key.to_x962_uncompressed()
166    }
167
168    /// Serialize this key in SubjectPublicKeyInfo format.
169    pub fn to_der_subject_public_key_info(&self) -> Buffer {
170        self.key.to_der_subject_public_key_info()
171    }
172
173    /// Return the public key corresponding to this private key.
174    pub fn to_public_key(&self) -> PublicKey<C> {
175        PublicKey {
176            point: self.key.to_point(),
177            marker: PhantomData,
178        }
179    }
180
181    /// Sign a digest of `to_be_signed` using this key and return the signature.
182    /// SHA-256 will be used to produce the digest if the curve of this public
183    /// key is P-256. SHA-384 will be used to produce the digest if the curve
184    /// of this public key is P-384.
185    pub fn sign(&self, to_be_signed: &[u8]) -> Vec<u8> {
186        // Safety: `self.key` is valid by construction.
187        let max_size = unsafe { bssl_sys::ECDSA_size(self.key.as_ffi_ptr()) };
188        // No curve can be empty.
189        assert_ne!(max_size, 0);
190
191        let digest = C::hash(to_be_signed);
192
193        unsafe {
194            with_output_vec(max_size, |out_buf| {
195                let mut out_len: core::ffi::c_uint = 0;
196                // Safety: `out_buf` points to at least `max_size` bytes,
197                // as required.
198                let result = {
199                    bssl_sys::ECDSA_sign(
200                        /*type=*/ 0,
201                        digest.as_slice().as_ffi_ptr(),
202                        digest.len(),
203                        out_buf,
204                        &mut out_len,
205                        self.key.as_ffi_ptr(),
206                    )
207                };
208                // Signing should never fail unless we're out of memory,
209                // which this crate doesn't handle.
210                assert_eq!(result, 1);
211                let out_len = out_len as usize;
212                assert!(out_len <= max_size);
213                // Safety: `out_len` bytes have been written.
214                out_len
215            })
216        }
217    }
218}
219
220#[cfg(test)]
221mod test {
222    use super::*;
223    use crate::ec::{P256, P384};
224
225    fn check_curve<C: ec::Curve>() {
226        let signed_message = b"hello world";
227        let key = PrivateKey::<C>::generate();
228        let mut sig = key.sign(signed_message);
229
230        let public_key = PublicKey::<C>::from_der_subject_public_key_info(
231            key.to_der_subject_public_key_info().as_ref(),
232        )
233        .unwrap();
234        assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
235
236        sig[10] ^= 1;
237        assert!(public_key.verify(signed_message, sig.as_slice()).is_err());
238    }
239
240    #[test]
241    fn p256() {
242        check_curve::<P256>();
243    }
244
245    #[test]
246    fn p384() {
247        check_curve::<P384>();
248    }
249}