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        self.verify_already_hashed(&digest, signature)
104    }
105
106    /// Verify `signature` as a valid ASN.1-based signature of `hashed_msg` with
107    /// this public key. `hashed_msg` must already have been hashed with a
108    /// secure hash function: passing unhashed data here breaks the security
109    /// properties of the signature scheme. If you have unhashed data, use
110    /// `verify` instead.
111    ///
112    /// This function exists because sometimes the "wrong" hash function is used
113    /// with a key. A key should not be used with different hash functions and
114    /// there is an obvious correspondence between NIST's standard elliptic
115    /// curves and the SHA-2 family of hash functions that implicitly defines
116    /// the correct hash function to use with a given key. But, if mistakes have
117    /// been made, this function is here for you.
118    pub fn verify_already_hashed(
119        &self,
120        hashed_msg: &[u8],
121        signature: &[u8],
122    ) -> Result<(), InvalidSignatureError> {
123        let result = self.point.with_point_as_ec_key(|ec_key| unsafe {
124            // Safety: `ec_key` is valid per `with_point_as_ec_key`.
125            bssl_sys::ECDSA_verify(
126                /*type=*/ 0,
127                hashed_msg.as_ffi_ptr(),
128                hashed_msg.len(),
129                signature.as_ffi_ptr(),
130                signature.len(),
131                ec_key,
132            )
133        });
134        if result == 1 {
135            Ok(())
136        } else {
137            Err(InvalidSignatureError)
138        }
139    }
140
141    /// Verify `signature` as a valid P1363-based signature of a digest of
142    /// `signed_msg` with this public key. SHA-256 will be used to produce the
143    /// digest if the curve of this public key is P-256. SHA-384 will be used to
144    /// produce the digest if the curve of this public key is P-384.
145    pub fn verify_p1363(
146        &self,
147        signed_msg: &[u8],
148        signature: &[u8],
149    ) -> Result<(), InvalidSignatureError> {
150        let digest = C::hash(signed_msg);
151        let result = self.point.with_point_as_ec_key(|ec_key| unsafe {
152            // Safety: `ec_key` is valid per `with_point_as_ec_key`.
153            bssl_sys::ECDSA_verify_p1363(
154                digest.as_slice().as_ffi_ptr(),
155                digest.len(),
156                signature.as_ffi_ptr(),
157                signature.len(),
158                ec_key,
159            )
160        });
161        if result == 1 {
162            Ok(())
163        } else {
164            Err(InvalidSignatureError)
165        }
166    }
167}
168
169/// An ECDSA private key over the given curve.
170pub struct PrivateKey<C: ec::Curve> {
171    key: ec::Key,
172    marker: PhantomData<C>,
173}
174
175impl<C: ec::Curve> Clone for PrivateKey<C> {
176    fn clone(&self) -> Self {
177        Self {
178            key: self.key.clone(),
179            marker: PhantomData,
180        }
181    }
182}
183
184/// Parsed `ECPrivateKey` dispatched into the corresponding curve types.
185pub enum ParsedPrivateKey {
186    /// A P-256 private key.
187    P256(PrivateKey<ec::P256>),
188    /// A P-384 private key.
189    P384(PrivateKey<ec::P384>),
190}
191
192impl ParsedPrivateKey {
193    /// Parses an ECPrivateKey structure from a DER encoded structure per [RFC 5915],
194    /// whose curve is specified by the `ECParameters`.
195    ///
196    /// Unless the curve group is one of the variants of [`Group`], this method returns [`None`].
197    ///
198    /// [RFC 5915]: <https://datatracker.ietf.org/doc/html/rfc5915>
199    pub fn from_der(der: &[u8]) -> Option<Self> {
200        let key = ec::Key::from_der_ec_private_key_with_curve_names(der)?;
201        match key.get_group()? {
202            Group::P256 => Some(ParsedPrivateKey::P256(PrivateKey {
203                key,
204                marker: PhantomData,
205            })),
206            Group::P384 => Some(ParsedPrivateKey::P384(PrivateKey {
207                key,
208                marker: PhantomData,
209            })),
210        }
211    }
212}
213
214impl<C: ec::Curve> PrivateKey<C> {
215    /// Generate a random private key.
216    pub fn generate() -> Self {
217        Self {
218            key: ec::Key::generate(C::group()),
219            marker: PhantomData,
220        }
221    }
222
223    /// Parse a `PrivateKey` from a zero-padded, big-endian representation of the secret scalar.
224    pub fn from_big_endian(scalar: &[u8]) -> Option<Self> {
225        let key = ec::Key::from_big_endian(C::group(), scalar)?;
226        Some(Self {
227            key,
228            marker: PhantomData,
229        })
230    }
231
232    /// Return the private key as zero-padded, big-endian bytes.
233    pub fn to_big_endian(&self) -> Buffer {
234        self.key.to_big_endian()
235    }
236
237    /// Parse an ECPrivateKey structure (from RFC 5915). The key must be on the
238    /// specified curve.
239    pub fn from_der_ec_private_key(der: &[u8]) -> Option<Self> {
240        let key = ec::Key::from_der_ec_private_key(C::group(), der)?;
241        Some(Self {
242            key,
243            marker: PhantomData,
244        })
245    }
246
247    /// Serialize this private key as an ECPrivateKey structure (from RFC 5915).
248    pub fn to_der_ec_private_key(&self) -> Buffer {
249        self.key.to_der_ec_private_key()
250    }
251
252    /// Parse a PrivateKeyInfo structure (from RFC 5208), commonly called
253    /// "PKCS#8 format". The key must be on the specified curve.
254    pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
255        let key = ec::Key::from_der_private_key_info(C::group(), der)?;
256        Some(Self {
257            key,
258            marker: PhantomData,
259        })
260    }
261
262    // Caller must make sure that the group of the key matches `C`,
263    // or else it panics.
264    pub(crate) fn from_ec_key(key: ec::Key) -> Self {
265        assert_eq!(key.get_group(), Some(C::group()));
266        Self {
267            key,
268            marker: PhantomData,
269        }
270    }
271
272    /// Serialize this private key as a PrivateKeyInfo structure (from RFC 5208),
273    /// commonly called "PKCS#8 format".
274    pub fn to_der_private_key_info(&self) -> Buffer {
275        self.key.to_der_private_key_info()
276    }
277
278    /// Serialize the _public_ part of this key in uncompressed X9.62 format.
279    pub fn to_x962_uncompressed(&self) -> Buffer {
280        self.key.to_x962_uncompressed()
281    }
282
283    /// Serialize the _public_ part of this key in compressed X9.62 format.
284    ///
285    /// WARNING: compressed form is rarely used and is not as well supported as
286    /// the uncompressed form.
287    pub fn to_x962_compressed(&self) -> Buffer {
288        self.key.to_x962_compressed()
289    }
290
291    /// Serialize this key in SubjectPublicKeyInfo format.
292    pub fn to_der_subject_public_key_info(&self) -> Buffer {
293        self.key.to_der_subject_public_key_info()
294    }
295
296    /// Return the public key corresponding to this private key.
297    pub fn to_public_key(&self) -> PublicKey<C> {
298        PublicKey {
299            point: self.key.to_point(),
300            marker: PhantomData,
301        }
302    }
303
304    /// Sign a digest of `to_be_signed` using this key and return the
305    /// ASN.1-based signature. SHA-256 will be used to produce the digest if the
306    /// curve of this public key is P-256. SHA-384 will be used to produce the
307    /// digest if the curve of this public key is P-384.
308    pub fn sign(&self, to_be_signed: &[u8]) -> Vec<u8> {
309        // Safety: `self.key` is valid by construction.
310        let max_size = unsafe { bssl_sys::ECDSA_size(self.key.as_ffi_ptr()) };
311        // No curve can be empty.
312        assert_ne!(max_size, 0);
313
314        let digest = C::hash(to_be_signed);
315
316        unsafe {
317            with_output_vec(max_size, |out_buf| {
318                let mut out_len: core::ffi::c_uint = 0;
319                // Safety: `out_buf` points to at least `max_size` bytes,
320                // as required.
321                let result = {
322                    bssl_sys::ECDSA_sign(
323                        /*type=*/ 0,
324                        digest.as_slice().as_ffi_ptr(),
325                        digest.len(),
326                        out_buf,
327                        &mut out_len,
328                        self.key.as_ffi_ptr(),
329                    )
330                };
331                // Signing should never fail unless we're out of memory,
332                // which this crate doesn't handle.
333                assert_eq!(result, 1);
334                let out_len = out_len as usize;
335                assert!(out_len <= max_size);
336                // Safety: `out_len` bytes have been written.
337                out_len
338            })
339        }
340    }
341
342    /// Sign a digest of `to_be_signed` using this key and return the
343    /// P1363-based signature. SHA-256 will be used to produce the digest if
344    /// the curve of this public key is P-256. SHA-384 will be used to produce
345    /// the digest if the curve of this public key is P-384.
346    pub fn sign_p1363(&self, to_be_signed: &[u8]) -> Vec<u8> {
347        // Safety: `self.key` is valid by construction.
348        let max_size = unsafe { bssl_sys::ECDSA_size_p1363(self.key.as_ffi_ptr()) };
349        // No curve can be empty.
350        assert_ne!(max_size, 0);
351
352        let digest = C::hash(to_be_signed);
353
354        unsafe {
355            with_output_vec(max_size, |out_buf| {
356                let mut out_len = 0usize;
357                // Safety: `out_buf` points to at least `size` bytes, as
358                // required.
359                let result = {
360                    bssl_sys::ECDSA_sign_p1363(
361                        digest.as_slice().as_ffi_ptr(),
362                        digest.len(),
363                        out_buf,
364                        &mut out_len,
365                        max_size,
366                        self.key.as_ffi_ptr(),
367                    )
368                };
369                // Signing should never fail unless we're out of memory,
370                // which this crate doesn't handle.
371                assert_eq!(result, 1);
372                assert!(out_len <= max_size);
373                // Safety: `out_len` bytes have been written.
374                out_len
375            })
376        }
377    }
378}
379
380#[cfg(test)]
381mod test {
382    use super::*;
383    use crate::ec::{P256, P384};
384
385    fn check_curve<C: ec::Curve>() {
386        let signed_message = b"hello world";
387        let key = PrivateKey::<C>::generate();
388        let mut sig = key.sign(signed_message);
389        let mut sig_p1363 = key.sign_p1363(signed_message);
390
391        let public_key = PublicKey::<C>::from_der_subject_public_key_info(
392            key.to_der_subject_public_key_info().as_ref(),
393        )
394        .unwrap();
395        assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
396        let mut digest = C::hash(signed_message);
397        assert!(public_key
398            .verify_already_hashed(digest.as_slice(), sig.as_slice())
399            .is_ok());
400        assert!(public_key
401            .verify_p1363(signed_message, sig_p1363.as_slice())
402            .is_ok());
403
404        digest[0] ^= 1;
405        assert!(public_key
406            .verify_already_hashed(digest.as_slice(), sig.as_slice())
407            .is_err());
408        sig[10] ^= 1;
409        assert!(public_key.verify(signed_message, sig.as_slice()).is_err());
410        sig_p1363[10] ^= 1;
411        assert!(public_key
412            .verify_p1363(signed_message, sig_p1363.as_slice())
413            .is_err());
414    }
415
416    fn check_compressed<C: ec::Curve>() {
417        let signed_message = b"hello world";
418        let key = PrivateKey::<C>::generate();
419        let mut sig = key.sign(signed_message);
420        let mut sig_p1363 = key.sign_p1363(signed_message);
421
422        let public_key =
423            PublicKey::<C>::from_x962_compressed(key.to_x962_compressed().as_ref()).unwrap();
424        assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
425        assert!(public_key
426            .verify_p1363(signed_message, sig_p1363.as_slice())
427            .is_ok());
428
429        sig[10] ^= 1;
430        assert!(public_key.verify(signed_message, sig.as_slice()).is_err());
431        sig_p1363[10] ^= 1;
432        assert!(public_key
433            .verify_p1363(signed_message, sig_p1363.as_slice())
434            .is_err());
435    }
436
437    fn check_parsing<C: ec::Curve>() {
438        let key = PrivateKey::<C>::generate();
439        let der = key.to_der_ec_private_key();
440        let parsed = ParsedPrivateKey::from_der(der.as_ref()).unwrap();
441        match parsed {
442            ParsedPrivateKey::P256(_) => assert!(matches!(C::group(), Group::P256)),
443            ParsedPrivateKey::P384(_) => assert!(matches!(C::group(), Group::P384)),
444        }
445    }
446
447    #[test]
448    fn p256() {
449        check_curve::<P256>();
450        check_compressed::<P256>();
451        check_parsing::<P256>();
452    }
453
454    #[test]
455    fn p384() {
456        check_curve::<P384>();
457        check_compressed::<P384>();
458        check_parsing::<P384>();
459    }
460}