]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
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_value() 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_value() 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! { | |
fe692bf9 | 113 | if #[cfg(any(ossl110, libressl273, boringssl))] { |
0a29b90c FG |
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_EC2M", 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_EC2M", 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_EC2M", 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 | } |