| 1 | //! Low level Elliptic Curve Digital Signature Algorithm (ECDSA) functions. |
| 2 | |
| 3 | use cfg_if::cfg_if; |
| 4 | use foreign_types::{ForeignType, ForeignTypeRef}; |
| 5 | use libc::c_int; |
| 6 | use std::mem; |
| 7 | use std::ptr; |
| 8 | |
| 9 | use crate::bn::{BigNum, BigNumRef}; |
| 10 | use crate::ec::EcKeyRef; |
| 11 | use crate::error::ErrorStack; |
| 12 | use crate::pkey::{HasPrivate, HasPublic}; |
| 13 | use crate::util::ForeignTypeRefExt; |
| 14 | use crate::{cvt_n, cvt_p, LenType}; |
| 15 | use openssl_macros::corresponds; |
| 16 | |
| 17 | foreign_type_and_impl_send_sync! { |
| 18 | type CType = ffi::ECDSA_SIG; |
| 19 | fn drop = ffi::ECDSA_SIG_free; |
| 20 | |
| 21 | /// A low level interface to ECDSA. |
| 22 | pub struct EcdsaSig; |
| 23 | /// A reference to an [`EcdsaSig`]. |
| 24 | pub struct EcdsaSigRef; |
| 25 | } |
| 26 | |
| 27 | impl EcdsaSig { |
| 28 | /// Computes a digital signature of the hash value `data` using the private EC key eckey. |
| 29 | #[corresponds (ECDSA_do_sign)] |
| 30 | pub fn sign<T>(data: &[u8], eckey: &EcKeyRef<T>) -> Result<EcdsaSig, ErrorStack> |
| 31 | where |
| 32 | T: HasPrivate, |
| 33 | { |
| 34 | unsafe { |
| 35 | assert!(data.len() <= c_int::MAX as usize); |
| 36 | let sig = cvt_p(ffi::ECDSA_do_sign( |
| 37 | data.as_ptr(), |
| 38 | data.len() as LenType, |
| 39 | eckey.as_ptr(), |
| 40 | ))?; |
| 41 | Ok(EcdsaSig::from_ptr(sig)) |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | /// Returns a new `EcdsaSig` by setting the `r` and `s` values associated with an ECDSA signature. |
| 46 | #[corresponds (ECDSA_SIG_set0)] |
| 47 | pub fn from_private_components(r: BigNum, s: BigNum) -> Result<EcdsaSig, ErrorStack> { |
| 48 | unsafe { |
| 49 | let sig = cvt_p(ffi::ECDSA_SIG_new())?; |
| 50 | ECDSA_SIG_set0(sig, r.as_ptr(), s.as_ptr()); |
| 51 | mem::forget((r, s)); |
| 52 | Ok(EcdsaSig::from_ptr(sig)) |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | from_der! { |
| 57 | /// Decodes a DER-encoded ECDSA signature. |
| 58 | #[corresponds (d2i_ECDSA_SIG)] |
| 59 | from_der, |
| 60 | EcdsaSig, |
| 61 | ffi::d2i_ECDSA_SIG |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | impl EcdsaSigRef { |
| 66 | to_der! { |
| 67 | /// Serializes the ECDSA signature into a DER-encoded ECDSASignature structure. |
| 68 | #[corresponds (i2d_ECDSA_SIG)] |
| 69 | to_der, |
| 70 | ffi::i2d_ECDSA_SIG |
| 71 | } |
| 72 | |
| 73 | /// Verifies if the signature is a valid ECDSA signature using the given public key. |
| 74 | #[corresponds (ECDSA_do_verify)] |
| 75 | pub fn verify<T>(&self, data: &[u8], eckey: &EcKeyRef<T>) -> Result<bool, ErrorStack> |
| 76 | where |
| 77 | T: HasPublic, |
| 78 | { |
| 79 | unsafe { |
| 80 | assert!(data.len() <= c_int::MAX as usize); |
| 81 | cvt_n(ffi::ECDSA_do_verify( |
| 82 | data.as_ptr(), |
| 83 | data.len() as LenType, |
| 84 | self.as_ptr(), |
| 85 | eckey.as_ptr(), |
| 86 | )) |
| 87 | .map(|x| x == 1) |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | /// Returns internal component: `r` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) |
| 92 | #[corresponds (ECDSA_SIG_get0)] |
| 93 | pub fn r(&self) -> &BigNumRef { |
| 94 | unsafe { |
| 95 | let mut r = ptr::null(); |
| 96 | ECDSA_SIG_get0(self.as_ptr(), &mut r, ptr::null_mut()); |
| 97 | BigNumRef::from_const_ptr(r) |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | /// Returns internal components: `s` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) |
| 102 | #[corresponds (ECDSA_SIG_get0)] |
| 103 | pub fn s(&self) -> &BigNumRef { |
| 104 | unsafe { |
| 105 | let mut s = ptr::null(); |
| 106 | ECDSA_SIG_get0(self.as_ptr(), ptr::null_mut(), &mut s); |
| 107 | BigNumRef::from_const_ptr(s) |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | cfg_if! { |
| 113 | if #[cfg(any(ossl110, libressl273, boringssl, awslc))] { |
| 114 | use ffi::{ECDSA_SIG_set0, ECDSA_SIG_get0}; |
| 115 | } else { |
| 116 | #[allow(bad_style)] |
| 117 | unsafe fn ECDSA_SIG_set0( |
| 118 | sig: *mut ffi::ECDSA_SIG, |
| 119 | r: *mut ffi::BIGNUM, |
| 120 | s: *mut ffi::BIGNUM, |
| 121 | ) -> c_int { |
| 122 | if r.is_null() || s.is_null() { |
| 123 | return 0; |
| 124 | } |
| 125 | ffi::BN_clear_free((*sig).r); |
| 126 | ffi::BN_clear_free((*sig).s); |
| 127 | (*sig).r = r; |
| 128 | (*sig).s = s; |
| 129 | 1 |
| 130 | } |
| 131 | |
| 132 | #[allow(bad_style)] |
| 133 | unsafe fn ECDSA_SIG_get0( |
| 134 | sig: *const ffi::ECDSA_SIG, |
| 135 | pr: *mut *const ffi::BIGNUM, |
| 136 | ps: *mut *const ffi::BIGNUM) |
| 137 | { |
| 138 | if !pr.is_null() { |
| 139 | (*pr) = (*sig).r; |
| 140 | } |
| 141 | if !ps.is_null() { |
| 142 | (*ps) = (*sig).s; |
| 143 | } |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | #[cfg (test)] |
| 149 | mod test { |
| 150 | use super::*; |
| 151 | use crate::ec::EcGroup; |
| 152 | use crate::ec::EcKey; |
| 153 | use crate::nid::Nid; |
| 154 | use crate::pkey::{Private, Public}; |
| 155 | |
| 156 | fn get_public_key(group: &EcGroup, x: &EcKey<Private>) -> Result<EcKey<Public>, ErrorStack> { |
| 157 | EcKey::from_public_key(group, x.public_key()) |
| 158 | } |
| 159 | |
| 160 | #[test ] |
| 161 | #[cfg_attr (osslconf = "OPENSSL_NO_EC" , ignore)] |
| 162 | fn sign_and_verify() { |
| 163 | let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); |
| 164 | let private_key = EcKey::generate(&group).unwrap(); |
| 165 | let public_key = get_public_key(&group, &private_key).unwrap(); |
| 166 | |
| 167 | let private_key2 = EcKey::generate(&group).unwrap(); |
| 168 | let public_key2 = get_public_key(&group, &private_key2).unwrap(); |
| 169 | |
| 170 | let data = String::from("hello" ); |
| 171 | let res = EcdsaSig::sign(data.as_bytes(), &private_key).unwrap(); |
| 172 | |
| 173 | // Signature can be verified using the correct data & correct public key |
| 174 | let verification = res.verify(data.as_bytes(), &public_key).unwrap(); |
| 175 | assert!(verification); |
| 176 | |
| 177 | // Signature will not be verified using the incorrect data but the correct public key |
| 178 | let verification2 = res |
| 179 | .verify(String::from("hello2" ).as_bytes(), &public_key) |
| 180 | .unwrap(); |
| 181 | assert!(!verification2); |
| 182 | |
| 183 | // Signature will not be verified using the correct data but the incorrect public key |
| 184 | let verification3 = res.verify(data.as_bytes(), &public_key2).unwrap(); |
| 185 | assert!(!verification3); |
| 186 | } |
| 187 | |
| 188 | #[test ] |
| 189 | #[cfg_attr (osslconf = "OPENSSL_NO_EC" , ignore)] |
| 190 | fn check_private_components() { |
| 191 | let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); |
| 192 | let private_key = EcKey::generate(&group).unwrap(); |
| 193 | let public_key = get_public_key(&group, &private_key).unwrap(); |
| 194 | let data = String::from("hello" ); |
| 195 | let res = EcdsaSig::sign(data.as_bytes(), &private_key).unwrap(); |
| 196 | |
| 197 | let verification = res.verify(data.as_bytes(), &public_key).unwrap(); |
| 198 | assert!(verification); |
| 199 | |
| 200 | let r = res.r().to_owned().unwrap(); |
| 201 | let s = res.s().to_owned().unwrap(); |
| 202 | |
| 203 | let res2 = EcdsaSig::from_private_components(r, s).unwrap(); |
| 204 | let verification2 = res2.verify(data.as_bytes(), &public_key).unwrap(); |
| 205 | assert!(verification2); |
| 206 | } |
| 207 | |
| 208 | #[test ] |
| 209 | #[cfg_attr (osslconf = "OPENSSL_NO_EC" , ignore)] |
| 210 | fn serialize_deserialize() { |
| 211 | let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); |
| 212 | let private_key = EcKey::generate(&group).unwrap(); |
| 213 | let public_key = get_public_key(&group, &private_key).unwrap(); |
| 214 | |
| 215 | let data = String::from("hello" ); |
| 216 | let res = EcdsaSig::sign(data.as_bytes(), &private_key).unwrap(); |
| 217 | |
| 218 | let der = res.to_der().unwrap(); |
| 219 | let sig = EcdsaSig::from_der(&der).unwrap(); |
| 220 | |
| 221 | let verification = sig.verify(data.as_bytes(), &public_key).unwrap(); |
| 222 | assert!(verification); |
| 223 | } |
| 224 | } |
| 225 | |