1 use cfg_if::cfg_if;
2 use foreign_types::{ForeignType, ForeignTypeRef};
3 use std::mem;
4 use std::ptr;
5 
6 use crate::bn::{BigNum, BigNumRef};
7 use crate::error::ErrorStack;
8 use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private};
9 use crate::{cvt, cvt_p};
10 
11 generic_foreign_type_and_impl_send_sync! {
12     type CType = ffi::DH;
13     fn drop = ffi::DH_free;
14 
15     pub struct Dh<T>;
16 
17     pub struct DhRef<T>;
18 }
19 
20 impl<T> DhRef<T>
21 where
22     T: HasParams,
23 {
24     to_pem! {
25         /// Serializes the parameters into a PEM-encoded PKCS#3 DHparameter structure.
26         ///
27         /// The output will have a header of `-----BEGIN DH PARAMETERS-----`.
28         ///
29         /// This corresponds to [`PEM_write_bio_DHparams`].
30         ///
31         /// [`PEM_write_bio_DHparams`]: https://www.openssl.org/docs/manmaster/man3/PEM_write_bio_DHparams.html
32         params_to_pem,
33         ffi::PEM_write_bio_DHparams
34     }
35 
36     to_der! {
37         /// Serializes the parameters into a DER-encoded PKCS#3 DHparameter structure.
38         ///
39         /// This corresponds to [`i2d_DHparams`].
40         ///
41         /// [`i2d_DHparams`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_DHparams.html
42         params_to_der,
43         ffi::i2d_DHparams
44     }
45 }
46 
47 impl Dh<Params> {
from_params(p: BigNum, g: BigNum, q: BigNum) -> Result<Dh<Params>, ErrorStack>48     pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result<Dh<Params>, ErrorStack> {
49         Self::from_pqg(p, Some(q), g)
50     }
51 
52     /// Creates a DH instance based upon the given primes and generator params.
53     ///
54     /// This corresponds to [`DH_new`] and [`DH_set0_pqg`].
55     ///
56     /// [`DH_new`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_new.html
57     /// [`DH_set0_pqg`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_set0_pqg.html
from_pqg( prime_p: BigNum, prime_q: Option<BigNum>, generator: BigNum, ) -> Result<Dh<Params>, ErrorStack>58     pub fn from_pqg(
59         prime_p: BigNum,
60         prime_q: Option<BigNum>,
61         generator: BigNum,
62     ) -> Result<Dh<Params>, ErrorStack> {
63         unsafe {
64             let dh = Dh::from_ptr(cvt_p(ffi::DH_new())?);
65             cvt(DH_set0_pqg(
66                 dh.0,
67                 prime_p.as_ptr(),
68                 prime_q.as_ref().map_or(ptr::null_mut(), |q| q.as_ptr()),
69                 generator.as_ptr(),
70             ))?;
71             mem::forget((prime_p, prime_q, generator));
72             Ok(dh)
73         }
74     }
75 
76     /// Sets the private key on the DH object and recomputes the public key.
set_private_key(self, priv_key: BigNum) -> Result<Dh<Private>, ErrorStack>77     pub fn set_private_key(self, priv_key: BigNum) -> Result<Dh<Private>, ErrorStack> {
78         unsafe {
79             let dh_ptr = self.0;
80             cvt(DH_set0_key(dh_ptr, ptr::null_mut(), priv_key.as_ptr()))?;
81             mem::forget(priv_key);
82 
83             cvt(ffi::DH_generate_key(dh_ptr))?;
84             mem::forget(self);
85             Ok(Dh::from_ptr(dh_ptr))
86         }
87     }
88 
89     /// Generates DH params based on the given `prime_len` and a fixed `generator` value.
90     ///
91     /// This corresponds to [`DH_generate_parameters_ex`].
92     ///
93     /// [`DH_generate_parameters_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_generate_parameters.html
generate_params(prime_len: u32, generator: u32) -> Result<Dh<Params>, ErrorStack>94     pub fn generate_params(prime_len: u32, generator: u32) -> Result<Dh<Params>, ErrorStack> {
95         unsafe {
96             let dh = Dh::from_ptr(cvt_p(ffi::DH_new())?);
97             cvt(ffi::DH_generate_parameters_ex(
98                 dh.0,
99                 prime_len as i32,
100                 generator as i32,
101                 ptr::null_mut(),
102             ))?;
103             Ok(dh)
104         }
105     }
106 
107     /// Generates a public and a private key based on the DH params.
108     ///
109     /// This corresponds to [`DH_generate_key`].
110     ///
111     /// [`DH_generate_key`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_generate_key.html
generate_key(self) -> Result<Dh<Private>, ErrorStack>112     pub fn generate_key(self) -> Result<Dh<Private>, ErrorStack> {
113         unsafe {
114             let dh_ptr = self.0;
115             cvt(ffi::DH_generate_key(dh_ptr))?;
116             mem::forget(self);
117             Ok(Dh::from_ptr(dh_ptr))
118         }
119     }
120 
121     from_pem! {
122         /// Deserializes a PEM-encoded PKCS#3 DHpararameters structure.
123         ///
124         /// The input should have a header of `-----BEGIN DH PARAMETERS-----`.
125         ///
126         /// This corresponds to [`PEM_read_bio_DHparams`].
127         ///
128         /// [`PEM_read_bio_DHparams`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_DHparams.html
129         params_from_pem,
130         Dh<Params>,
131         ffi::PEM_read_bio_DHparams
132     }
133 
134     from_der! {
135         /// Deserializes a DER-encoded PKCS#3 DHparameters structure.
136         ///
137         /// This corresponds to [`d2i_DHparams`].
138         ///
139         /// [`d2i_DHparams`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_DHparams.html
140         params_from_der,
141         Dh<Params>,
142         ffi::d2i_DHparams
143     }
144 
145     /// Requires OpenSSL 1.0.2 or newer.
146     #[cfg(any(ossl102, ossl110))]
get_1024_160() -> Result<Dh<Params>, ErrorStack>147     pub fn get_1024_160() -> Result<Dh<Params>, ErrorStack> {
148         unsafe {
149             ffi::init();
150             cvt_p(ffi::DH_get_1024_160()).map(|p| Dh::from_ptr(p))
151         }
152     }
153 
154     /// Requires OpenSSL 1.0.2 or newer.
155     #[cfg(any(ossl102, ossl110))]
get_2048_224() -> Result<Dh<Params>, ErrorStack>156     pub fn get_2048_224() -> Result<Dh<Params>, ErrorStack> {
157         unsafe {
158             ffi::init();
159             cvt_p(ffi::DH_get_2048_224()).map(|p| Dh::from_ptr(p))
160         }
161     }
162 
163     /// Requires OpenSSL 1.0.2 or newer.
164     #[cfg(any(ossl102, ossl110))]
get_2048_256() -> Result<Dh<Params>, ErrorStack>165     pub fn get_2048_256() -> Result<Dh<Params>, ErrorStack> {
166         unsafe {
167             ffi::init();
168             cvt_p(ffi::DH_get_2048_256()).map(|p| Dh::from_ptr(p))
169         }
170     }
171 }
172 
173 impl<T> Dh<T>
174 where
175     T: HasParams,
176 {
177     /// Returns the prime `p` from the DH instance.
178     ///
179     /// This corresponds to [`DH_get0_pqg`].
180     ///
181     /// [`DH_get0_pqg`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html
prime_p(&self) -> &BigNumRef182     pub fn prime_p(&self) -> &BigNumRef {
183         let mut p = ptr::null();
184         unsafe {
185             DH_get0_pqg(self.as_ptr(), &mut p, ptr::null_mut(), ptr::null_mut());
186             BigNumRef::from_ptr(p as *mut _)
187         }
188     }
189 
190     /// Returns the prime `q` from the DH instance.
191     ///
192     /// This corresponds to [`DH_get0_pqg`].
193     ///
194     /// [`DH_get0_pqg`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html
prime_q(&self) -> Option<&BigNumRef>195     pub fn prime_q(&self) -> Option<&BigNumRef> {
196         let mut q = ptr::null();
197         unsafe {
198             DH_get0_pqg(self.as_ptr(), ptr::null_mut(), &mut q, ptr::null_mut());
199             if q.is_null() {
200                 None
201             } else {
202                 Some(BigNumRef::from_ptr(q as *mut _))
203             }
204         }
205     }
206 
207     /// Returns the generator from the DH instance.
208     ///
209     /// This corresponds to [`DH_get0_pqg`].
210     ///
211     /// [`DH_get0_pqg`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html
generator(&self) -> &BigNumRef212     pub fn generator(&self) -> &BigNumRef {
213         let mut g = ptr::null();
214         unsafe {
215             DH_get0_pqg(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut g);
216             BigNumRef::from_ptr(g as *mut _)
217         }
218     }
219 }
220 
221 impl<T> DhRef<T>
222 where
223     T: HasPublic,
224 {
225     /// Returns the public key from the DH instance.
226     ///
227     /// This corresponds to [`DH_get0_key`].
228     ///
229     /// [`DH_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_key.html
public_key(&self) -> &BigNumRef230     pub fn public_key(&self) -> &BigNumRef {
231         let mut pub_key = ptr::null();
232         unsafe {
233             DH_get0_key(self.as_ptr(), &mut pub_key, ptr::null_mut());
234             BigNumRef::from_ptr(pub_key as *mut _)
235         }
236     }
237 }
238 
239 impl<T> DhRef<T>
240 where
241     T: HasPrivate,
242 {
243     /// Computes a shared secret from the own private key and the given `public_key`.
244     ///
245     /// This corresponds to [`DH_compute_key`].
246     ///
247     /// [`DH_compute_key`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_compute_key.html
compute_key(&self, public_key: &BigNumRef) -> Result<Vec<u8>, ErrorStack>248     pub fn compute_key(&self, public_key: &BigNumRef) -> Result<Vec<u8>, ErrorStack> {
249         unsafe {
250             let key_len = ffi::DH_size(self.as_ptr());
251             let mut key = vec![0u8; key_len as usize];
252             cvt(ffi::DH_compute_key(
253                 key.as_mut_ptr(),
254                 public_key.as_ptr(),
255                 self.as_ptr(),
256             ))?;
257             Ok(key)
258         }
259     }
260 
261     /// Returns the private key from the DH instance.
262     ///
263     /// This corresponds to [`DH_get0_key`].
264     ///
265     /// [`DH_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_key.html
private_key(&self) -> &BigNumRef266     pub fn private_key(&self) -> &BigNumRef {
267         let mut priv_key = ptr::null();
268         unsafe {
269             DH_get0_key(self.as_ptr(), ptr::null_mut(), &mut priv_key);
270             BigNumRef::from_ptr(priv_key as *mut _)
271         }
272     }
273 }
274 
275 cfg_if! {
276     if #[cfg(any(ossl110, libressl270))] {
277         use ffi::{DH_set0_pqg, DH_get0_pqg, DH_get0_key, DH_set0_key};
278     } else {
279         #[allow(bad_style)]
280         unsafe fn DH_set0_pqg(
281             dh: *mut ffi::DH,
282             p: *mut ffi::BIGNUM,
283             q: *mut ffi::BIGNUM,
284             g: *mut ffi::BIGNUM,
285         ) -> ::libc::c_int {
286             (*dh).p = p;
287             (*dh).q = q;
288             (*dh).g = g;
289             1
290         }
291 
292         #[allow(bad_style)]
293         unsafe fn DH_get0_pqg(
294             dh: *mut ffi::DH,
295             p: *mut *const ffi::BIGNUM,
296             q: *mut *const ffi::BIGNUM,
297             g: *mut *const ffi::BIGNUM,
298         ) {
299             if !p.is_null() {
300                 *p = (*dh).p;
301             }
302             if !q.is_null() {
303                 *q = (*dh).q;
304             }
305             if !g.is_null() {
306                 *g = (*dh).g;
307             }
308         }
309 
310         #[allow(bad_style)]
311         unsafe fn DH_set0_key(
312             dh: *mut ffi::DH,
313             pub_key: *mut ffi::BIGNUM,
314             priv_key: *mut ffi::BIGNUM,
315         ) -> ::libc::c_int {
316             (*dh).pub_key = pub_key;
317             (*dh).priv_key = priv_key;
318             1
319         }
320 
321         #[allow(bad_style)]
322         unsafe fn DH_get0_key(
323             dh: *mut ffi::DH,
324             pub_key: *mut *const ffi::BIGNUM,
325             priv_key: *mut *const ffi::BIGNUM,
326         ) {
327             if !pub_key.is_null() {
328                 *pub_key = (*dh).pub_key;
329             }
330             if !priv_key.is_null() {
331                 *priv_key = (*dh).priv_key;
332             }
333         }
334     }
335 }
336 
337 #[cfg(test)]
338 mod tests {
339     use crate::bn::BigNum;
340     use crate::dh::Dh;
341     use crate::ssl::{SslContext, SslMethod};
342 
343     #[test]
344     #[cfg(ossl102)]
test_dh_rfc5114()345     fn test_dh_rfc5114() {
346         let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
347         let dh2 = Dh::get_2048_224().unwrap();
348         ctx.set_tmp_dh(&dh2).unwrap();
349         let dh3 = Dh::get_2048_256().unwrap();
350         ctx.set_tmp_dh(&dh3).unwrap();
351     }
352 
353     #[test]
test_dh_params()354     fn test_dh_params() {
355         let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
356         let prime_p = BigNum::from_hex_str(
357             "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF\
358              4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B47\
359              58C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B6\
360              3ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5\
361              140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710\
362              C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597",
363         ).unwrap();
364         let prime_q = BigNum::from_hex_str(
365             "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED\
366              4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A\
367              57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5\
368              045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E\
369              052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67E\
370              B6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659",
371         ).unwrap();
372         let generator = BigNum::from_hex_str(
373             "8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3",
374         )
375         .unwrap();
376         let dh = Dh::from_params(
377             prime_p.to_owned().unwrap(),
378             generator.to_owned().unwrap(),
379             prime_q.to_owned().unwrap(),
380         )
381         .unwrap();
382         ctx.set_tmp_dh(&dh).unwrap();
383 
384         assert_eq!(dh.prime_p(), &prime_p);
385         assert_eq!(dh.prime_q().unwrap(), &prime_q);
386         assert_eq!(dh.generator(), &generator);
387     }
388 
389     #[test]
390     #[cfg(ossl102)]
test_dh_stored_restored()391     fn test_dh_stored_restored() {
392         let dh1 = Dh::get_2048_256().unwrap();
393         let key1 = dh1.generate_key().unwrap();
394 
395         let dh2 = Dh::get_2048_256().unwrap();
396         let key2 = dh2
397             .set_private_key(key1.private_key().to_owned().unwrap())
398             .unwrap();
399 
400         assert_eq!(key1.public_key(), key2.public_key());
401         assert_eq!(key1.private_key(), key2.private_key());
402     }
403 
404     #[test]
test_dh_from_pem()405     fn test_dh_from_pem() {
406         let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
407         let params = include_bytes!("../test/dhparams.pem");
408         let dh = Dh::params_from_pem(params).unwrap();
409         ctx.set_tmp_dh(&dh).unwrap();
410     }
411 
412     #[test]
test_dh_from_der()413     fn test_dh_from_der() {
414         let params = include_bytes!("../test/dhparams.pem");
415         let dh = Dh::params_from_pem(params).unwrap();
416         let der = dh.params_to_der().unwrap();
417         Dh::params_from_der(&der).unwrap();
418     }
419 
420     #[test]
421     #[cfg(ossl102)]
test_dh_generate_key_compute_key()422     fn test_dh_generate_key_compute_key() {
423         let dh1 = Dh::get_2048_224().unwrap().generate_key().unwrap();
424         let dh2 = Dh::get_2048_224().unwrap().generate_key().unwrap();
425 
426         let shared_a = dh1.compute_key(dh2.public_key()).unwrap();
427         let shared_b = dh2.compute_key(dh1.public_key()).unwrap();
428 
429         assert_eq!(shared_a, shared_b);
430     }
431 
432     #[test]
test_dh_generate_params_generate_key_compute_key()433     fn test_dh_generate_params_generate_key_compute_key() {
434         let dh_params1 = Dh::generate_params(512, 2).unwrap();
435         let dh_params2 = Dh::from_pqg(
436             dh_params1.prime_p().to_owned().unwrap(),
437             None,
438             dh_params1.generator().to_owned().unwrap(),
439         )
440         .unwrap();
441 
442         let dh1 = dh_params1.generate_key().unwrap();
443         let dh2 = dh_params2.generate_key().unwrap();
444 
445         let shared_a = dh1.compute_key(dh2.public_key()).unwrap();
446         let shared_b = dh2.compute_key(dh1.public_key()).unwrap();
447 
448         assert_eq!(shared_a, shared_b);
449     }
450 }
451