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