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}