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 
36 use crate::{ec, sealed, with_output_vec, Buffer, FfiSlice, InvalidSignatureError};
37 use alloc::vec::Vec;
38 use core::marker::PhantomData;
39 
40 /// An ECDSA public key over the given curve.
41 pub struct PublicKey<C: ec::Curve> {
42     point: ec::Point,
43     marker: PhantomData<C>,
44 }
45 
46 impl<C: ec::Curve> PublicKey<C> {
47     /// Parse a public key in uncompressed X9.62 format. (This is the common
48     /// format for elliptic curve points beginning with an 0x04 byte.)
from_x962_uncompressed(x962: &[u8]) -> Option<Self>49     pub fn from_x962_uncompressed(x962: &[u8]) -> Option<Self> {
50         let point = ec::Point::from_x962_uncompressed(C::group(sealed::Sealed), x962)?;
51         Some(Self {
52             point,
53             marker: PhantomData,
54         })
55     }
56 
57     /// Serialize this key as uncompressed X9.62 format.
to_x962_uncompressed(&self) -> Buffer58     pub fn to_x962_uncompressed(&self) -> Buffer {
59         self.point.to_x962_uncompressed()
60     }
61 
62     /// Parse a public key in SubjectPublicKeyInfo format.
63     /// (This is found in, e.g., X.509 certificates.)
from_der_subject_public_key_info(spki: &[u8]) -> Option<Self>64     pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option<Self> {
65         let point = ec::Point::from_der_subject_public_key_info(C::group(sealed::Sealed), spki)?;
66         Some(Self {
67             point,
68             marker: PhantomData,
69         })
70     }
71 
72     /// Serialize this key in SubjectPublicKeyInfo format.
to_der_subject_public_key_info(&self) -> Buffer73     pub fn to_der_subject_public_key_info(&self) -> Buffer {
74         self.point.to_der_subject_public_key_info()
75     }
76 
77     /// Verify `signature` as a valid ASN.1-based signature of a digest of
78     /// `signed_msg` with this public key. SHA-256 will be used to produce the
79     /// digest if the curve of this public key is P-256. SHA-384 will be used to
80     /// produce the digest if the curve of this public key is P-384.
verify(&self, signed_msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError>81     pub fn verify(&self, signed_msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> {
82         let digest = C::hash(signed_msg);
83         let result = self.point.with_point_as_ec_key(|ec_key| unsafe {
84             // Safety: `ec_key` is valid per `with_point_as_ec_key`.
85             bssl_sys::ECDSA_verify(
86                 /*type=*/ 0,
87                 digest.as_slice().as_ffi_ptr(),
88                 digest.len(),
89                 signature.as_ffi_ptr(),
90                 signature.len(),
91                 ec_key,
92             )
93         });
94         if result == 1 {
95             Ok(())
96         } else {
97             Err(InvalidSignatureError)
98         }
99     }
100 
101     /// Verify `signature` as a valid P1363-based signature of a digest of
102     /// `signed_msg` with this public key. SHA-256 will be used to produce the
103     /// digest if the curve of this public key is P-256. SHA-384 will be used to
104     /// produce the digest if the curve of this public key is P-384.
verify_p1363( &self, signed_msg: &[u8], signature: &[u8], ) -> Result<(), InvalidSignatureError>105     pub fn verify_p1363(
106         &self,
107         signed_msg: &[u8],
108         signature: &[u8],
109     ) -> Result<(), InvalidSignatureError> {
110         let digest = C::hash(signed_msg);
111         let result = self.point.with_point_as_ec_key(|ec_key| unsafe {
112             // Safety: `ec_key` is valid per `with_point_as_ec_key`.
113             bssl_sys::ECDSA_verify_p1363(
114                 digest.as_slice().as_ffi_ptr(),
115                 digest.len(),
116                 signature.as_ffi_ptr(),
117                 signature.len(),
118                 ec_key,
119             )
120         });
121         if result == 1 {
122             Ok(())
123         } else {
124             Err(InvalidSignatureError)
125         }
126     }
127 }
128 
129 /// An ECDH private key over the given curve.
130 pub struct PrivateKey<C: ec::Curve> {
131     key: ec::Key,
132     marker: PhantomData<C>,
133 }
134 
135 impl<C: ec::Curve> PrivateKey<C> {
136     /// Generate a random private key.
generate() -> Self137     pub fn generate() -> Self {
138         Self {
139             key: ec::Key::generate(C::group(sealed::Sealed)),
140             marker: PhantomData,
141         }
142     }
143 
144     /// Parse a `PrivateKey` from a zero-padded, big-endian representation of the secret scalar.
from_big_endian(scalar: &[u8]) -> Option<Self>145     pub fn from_big_endian(scalar: &[u8]) -> Option<Self> {
146         let key = ec::Key::from_big_endian(C::group(sealed::Sealed), scalar)?;
147         Some(Self {
148             key,
149             marker: PhantomData,
150         })
151     }
152 
153     /// Return the private key as zero-padded, big-endian bytes.
to_big_endian(&self) -> Buffer154     pub fn to_big_endian(&self) -> Buffer {
155         self.key.to_big_endian()
156     }
157 
158     /// Parse an ECPrivateKey structure (from RFC 5915). The key must be on the
159     /// specified curve.
from_der_ec_private_key(der: &[u8]) -> Option<Self>160     pub fn from_der_ec_private_key(der: &[u8]) -> Option<Self> {
161         let key = ec::Key::from_der_ec_private_key(C::group(sealed::Sealed), der)?;
162         Some(Self {
163             key,
164             marker: PhantomData,
165         })
166     }
167 
168     /// Serialize this private key as an ECPrivateKey structure (from RFC 5915).
to_der_ec_private_key(&self) -> Buffer169     pub fn to_der_ec_private_key(&self) -> Buffer {
170         self.key.to_der_ec_private_key()
171     }
172 
173     /// Parse a PrivateKeyInfo structure (from RFC 5208), commonly called
174     /// "PKCS#8 format". The key must be on the specified curve.
from_der_private_key_info(der: &[u8]) -> Option<Self>175     pub fn from_der_private_key_info(der: &[u8]) -> Option<Self> {
176         let key = ec::Key::from_der_private_key_info(C::group(sealed::Sealed), der)?;
177         Some(Self {
178             key,
179             marker: PhantomData,
180         })
181     }
182 
183     /// Serialize this private key as a PrivateKeyInfo structure (from RFC 5208),
184     /// commonly called "PKCS#8 format".
to_der_private_key_info(&self) -> Buffer185     pub fn to_der_private_key_info(&self) -> Buffer {
186         self.key.to_der_private_key_info()
187     }
188 
189     /// Serialize the _public_ part of this key in uncompressed X9.62 format.
to_x962_uncompressed(&self) -> Buffer190     pub fn to_x962_uncompressed(&self) -> Buffer {
191         self.key.to_x962_uncompressed()
192     }
193 
194     /// Serialize this key in SubjectPublicKeyInfo format.
to_der_subject_public_key_info(&self) -> Buffer195     pub fn to_der_subject_public_key_info(&self) -> Buffer {
196         self.key.to_der_subject_public_key_info()
197     }
198 
199     /// Return the public key corresponding to this private key.
to_public_key(&self) -> PublicKey<C>200     pub fn to_public_key(&self) -> PublicKey<C> {
201         PublicKey {
202             point: self.key.to_point(),
203             marker: PhantomData,
204         }
205     }
206 
207     /// Sign a digest of `to_be_signed` using this key and return the
208     /// ASN.1-based signature. SHA-256 will be used to produce the digest if the
209     /// curve of this public key is P-256. SHA-384 will be used to produce the
210     /// digest if the curve of this public key is P-384.
sign(&self, to_be_signed: &[u8]) -> Vec<u8>211     pub fn sign(&self, to_be_signed: &[u8]) -> Vec<u8> {
212         // Safety: `self.key` is valid by construction.
213         let max_size = unsafe { bssl_sys::ECDSA_size(self.key.as_ffi_ptr()) };
214         // No curve can be empty.
215         assert_ne!(max_size, 0);
216 
217         let digest = C::hash(to_be_signed);
218 
219         unsafe {
220             with_output_vec(max_size, |out_buf| {
221                 let mut out_len: core::ffi::c_uint = 0;
222                 // Safety: `out_buf` points to at least `max_size` bytes,
223                 // as required.
224                 let result = {
225                     bssl_sys::ECDSA_sign(
226                         /*type=*/ 0,
227                         digest.as_slice().as_ffi_ptr(),
228                         digest.len(),
229                         out_buf,
230                         &mut out_len,
231                         self.key.as_ffi_ptr(),
232                     )
233                 };
234                 // Signing should never fail unless we're out of memory,
235                 // which this crate doesn't handle.
236                 assert_eq!(result, 1);
237                 let out_len = out_len as usize;
238                 assert!(out_len <= max_size);
239                 // Safety: `out_len` bytes have been written.
240                 out_len
241             })
242         }
243     }
244 
245     /// Sign a digest of `to_be_signed` using this key and return the
246     /// P1363-based signature. SHA-256 will be used to produce the digest if
247     /// the curve of this public key is P-256. SHA-384 will be used to produce
248     /// the digest if the curve of this public key is P-384.
sign_p1363(&self, to_be_signed: &[u8]) -> Vec<u8>249     pub fn sign_p1363(&self, to_be_signed: &[u8]) -> Vec<u8> {
250         // Safety: `self.key` is valid by construction.
251         let max_size = unsafe { bssl_sys::ECDSA_size_p1363(self.key.as_ffi_ptr()) };
252         // No curve can be empty.
253         assert_ne!(max_size, 0);
254 
255         let digest = C::hash(to_be_signed);
256 
257         unsafe {
258             with_output_vec(max_size, |out_buf| {
259                 let mut out_len = 0usize;
260                 // Safety: `out_buf` points to at least `size` bytes, as
261                 // required.
262                 let result = {
263                     bssl_sys::ECDSA_sign_p1363(
264                         digest.as_slice().as_ffi_ptr(),
265                         digest.len(),
266                         out_buf,
267                         &mut out_len,
268                         max_size,
269                         self.key.as_ffi_ptr(),
270                     )
271                 };
272                 // Signing should never fail unless we're out of memory,
273                 // which this crate doesn't handle.
274                 assert_eq!(result, 1);
275                 assert!(out_len <= max_size);
276                 // Safety: `out_len` bytes have been written.
277                 out_len
278             })
279         }
280     }
281 }
282 
283 #[cfg(test)]
284 mod test {
285     use super::*;
286     use crate::ec::{P256, P384};
287 
check_curve<C: ec::Curve>()288     fn check_curve<C: ec::Curve>() {
289         let signed_message = b"hello world";
290         let key = PrivateKey::<C>::generate();
291         let mut sig = key.sign(signed_message);
292         let mut sig_p1363 = key.sign_p1363(signed_message);
293 
294         let public_key = PublicKey::<C>::from_der_subject_public_key_info(
295             key.to_der_subject_public_key_info().as_ref(),
296         )
297         .unwrap();
298         assert!(public_key.verify(signed_message, sig.as_slice()).is_ok());
299         assert!(public_key
300             .verify_p1363(signed_message, sig_p1363.as_slice())
301             .is_ok());
302 
303         sig[10] ^= 1;
304         assert!(public_key.verify(signed_message, sig.as_slice()).is_err());
305         sig_p1363[10] ^= 1;
306         assert!(public_key
307             .verify_p1363(signed_message, sig_p1363.as_slice())
308             .is_err());
309     }
310 
311     #[test]
p256()312     fn p256() {
313         check_curve::<P256>();
314     }
315 
316     #[test]
p384()317     fn p384() {
318         check_curve::<P384>();
319     }
320 }
321