1 use error::ErrorStack;
2 use ffi;
3 use foreign_types::{ForeignType, ForeignTypeRef};
4 use std::mem;
5 use std::ptr;
6 
7 use bn::{BigNum, BigNumRef};
8 use pkey::{HasParams, HasPrivate, HasPublic, Params, Private};
9 use {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     /// Generates DH params based on the given `prime_len` and a fixed `generator` value.
77     ///
78     /// This corresponds to [`DH_generate_parameters`].
79     ///
80     /// [`DH_generate_parameters`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_generate_parameters.html
generate_params(prime_len: u32, generator: u32) -> Result<Dh<Params>, ErrorStack>81     pub fn generate_params(prime_len: u32, generator: u32) -> Result<Dh<Params>, ErrorStack> {
82         unsafe {
83             Ok(Dh::from_ptr(cvt_p(ffi::DH_generate_parameters(
84                 prime_len as i32,
85                 generator as i32,
86                 None,
87                 ptr::null_mut(),
88             ))?))
89         }
90     }
91 
92     /// Generates a public and a private key based on the DH params.
93     ///
94     /// This corresponds to [`DH_generate_key`].
95     ///
96     /// [`DH_generate_key`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_generate_key.html
generate_key(self) -> Result<Dh<Private>, ErrorStack>97     pub fn generate_key(self) -> Result<Dh<Private>, ErrorStack> {
98         unsafe {
99             let dh_ptr = self.0;
100             cvt(ffi::DH_generate_key(dh_ptr))?;
101             mem::forget(self);
102             Ok(Dh::from_ptr(dh_ptr))
103         }
104     }
105 
106     from_pem! {
107         /// Deserializes a PEM-encoded PKCS#3 DHpararameters structure.
108         ///
109         /// The input should have a header of `-----BEGIN DH PARAMETERS-----`.
110         ///
111         /// This corresponds to [`PEM_read_bio_DHparams`].
112         ///
113         /// [`PEM_read_bio_DHparams`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_DHparams.html
114         params_from_pem,
115         Dh<Params>,
116         ffi::PEM_read_bio_DHparams
117     }
118 
119     from_der! {
120         /// Deserializes a DER-encoded PKCS#3 DHparameters structure.
121         ///
122         /// This corresponds to [`d2i_DHparams`].
123         ///
124         /// [`d2i_DHparams`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_DHparams.html
125         params_from_der,
126         Dh<Params>,
127         ffi::d2i_DHparams
128     }
129 
130     /// Requires OpenSSL 1.0.2 or newer.
131     #[cfg(any(ossl102, ossl110))]
get_1024_160() -> Result<Dh<Params>, ErrorStack>132     pub fn get_1024_160() -> Result<Dh<Params>, ErrorStack> {
133         unsafe {
134             ffi::init();
135             cvt_p(ffi::DH_get_1024_160()).map(|p| Dh::from_ptr(p))
136         }
137     }
138 
139     /// Requires OpenSSL 1.0.2 or newer.
140     #[cfg(any(ossl102, ossl110))]
get_2048_224() -> Result<Dh<Params>, ErrorStack>141     pub fn get_2048_224() -> Result<Dh<Params>, ErrorStack> {
142         unsafe {
143             ffi::init();
144             cvt_p(ffi::DH_get_2048_224()).map(|p| Dh::from_ptr(p))
145         }
146     }
147 
148     /// Requires OpenSSL 1.0.2 or newer.
149     #[cfg(any(ossl102, ossl110))]
get_2048_256() -> Result<Dh<Params>, ErrorStack>150     pub fn get_2048_256() -> Result<Dh<Params>, ErrorStack> {
151         unsafe {
152             ffi::init();
153             cvt_p(ffi::DH_get_2048_256()).map(|p| Dh::from_ptr(p))
154         }
155     }
156 }
157 
158 impl<T> Dh<T>
159 where
160     T: HasParams,
161 {
162     /// Returns the prime `p` from the DH instance.
163     ///
164     /// This corresponds to [`DH_get0_pqg`].
165     ///
166     /// [`DH_get0_pqg`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html
prime_p(&self) -> &BigNumRef167     pub fn prime_p(&self) -> &BigNumRef {
168         let mut p = ptr::null();
169         unsafe {
170             DH_get0_pqg(self.as_ptr(), &mut p, ptr::null_mut(), ptr::null_mut());
171             BigNumRef::from_ptr(p as *mut _)
172         }
173     }
174 
175     /// Returns the prime `q` from the DH instance.
176     ///
177     /// This corresponds to [`DH_get0_pqg`].
178     ///
179     /// [`DH_get0_pqg`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html
prime_q(&self) -> Option<&BigNumRef>180     pub fn prime_q(&self) -> Option<&BigNumRef> {
181         let mut q = ptr::null();
182         unsafe {
183             DH_get0_pqg(self.as_ptr(), ptr::null_mut(), &mut q, ptr::null_mut());
184             if q.is_null() {
185                 None
186             } else {
187                 Some(BigNumRef::from_ptr(q as *mut _))
188             }
189         }
190     }
191 
192     /// Returns the generator from the DH instance.
193     ///
194     /// This corresponds to [`DH_get0_pqg`].
195     ///
196     /// [`DH_get0_pqg`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_pqg.html
generator(&self) -> &BigNumRef197     pub fn generator(&self) -> &BigNumRef {
198         let mut g = ptr::null();
199         unsafe {
200             DH_get0_pqg(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut g);
201             BigNumRef::from_ptr(g as *mut _)
202         }
203     }
204 }
205 
206 impl<T> DhRef<T>
207 where
208     T: HasPublic,
209 {
210     /// Returns the public key from the DH instance.
211     ///
212     /// This corresponds to [`DH_get0_key`].
213     ///
214     /// [`DH_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/DH_get0_key.html
public_key(&self) -> &BigNumRef215     pub fn public_key(&self) -> &BigNumRef {
216         let mut pub_key = ptr::null();
217         unsafe {
218             DH_get0_key(self.as_ptr(), &mut pub_key, ptr::null_mut());
219             BigNumRef::from_ptr(pub_key as *mut _)
220         }
221     }
222 }
223 
224 impl<T> DhRef<T>
225 where
226     T: HasPrivate,
227 {
228     /// Computes a shared secret from the own private key and the given `public_key`.
229     ///
230     /// This corresponds to [`DH_compute_key`].
231     ///
232     /// [`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>233     pub fn compute_key(&self, public_key: &BigNumRef) -> Result<Vec<u8>, ErrorStack> {
234         unsafe {
235             let key_len = ffi::DH_size(self.as_ptr());
236             let mut key = vec![0u8; key_len as usize];
237             cvt(ffi::DH_compute_key(
238                 key.as_mut_ptr(),
239                 public_key.as_ptr(),
240                 self.as_ptr(),
241             ))?;
242             Ok(key)
243         }
244     }
245 }
246 
247 cfg_if! {
248     if #[cfg(any(ossl110, libressl270))] {
249         use ffi::{DH_set0_pqg, DH_get0_pqg, DH_get0_key};
250     } else {
251         #[allow(bad_style)]
252         unsafe fn DH_set0_pqg(
253             dh: *mut ffi::DH,
254             p: *mut ffi::BIGNUM,
255             q: *mut ffi::BIGNUM,
256             g: *mut ffi::BIGNUM,
257         ) -> ::libc::c_int {
258             (*dh).p = p;
259             (*dh).q = q;
260             (*dh).g = g;
261             1
262         }
263 
264         #[allow(bad_style)]
265         unsafe fn DH_get0_pqg(
266             dh: *mut ffi::DH,
267             p: *mut *const ffi::BIGNUM,
268             q: *mut *const ffi::BIGNUM,
269             g: *mut *const ffi::BIGNUM,
270         ) {
271             if !p.is_null() {
272                 *p = (*dh).p;
273             }
274             if !q.is_null() {
275                 *q = (*dh).q;
276             }
277             if !g.is_null() {
278                 *g = (*dh).g;
279             }
280         }
281 
282         #[allow(bad_style)]
283         unsafe fn DH_get0_key(
284             dh: *mut ffi::DH,
285             pub_key: *mut *const ffi::BIGNUM,
286             priv_key: *mut *const ffi::BIGNUM,
287         ) {
288             if !pub_key.is_null() {
289                 *pub_key = (*dh).pub_key;
290             }
291             if !priv_key.is_null() {
292                 *priv_key = (*dh).priv_key;
293             }
294         }
295     }
296 }
297 
298 #[cfg(test)]
299 mod tests {
300     use bn::BigNum;
301     use dh::Dh;
302     use ssl::{SslContext, SslMethod};
303 
304     #[test]
305     #[cfg(ossl102)]
test_dh_rfc5114()306     fn test_dh_rfc5114() {
307         let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
308         let dh2 = Dh::get_2048_224().unwrap();
309         ctx.set_tmp_dh(&dh2).unwrap();
310         let dh3 = Dh::get_2048_256().unwrap();
311         ctx.set_tmp_dh(&dh3).unwrap();
312     }
313 
314     #[test]
test_dh_params()315     fn test_dh_params() {
316         let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
317         let prime_p = BigNum::from_hex_str(
318             "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF\
319              4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B47\
320              58C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B6\
321              3ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5\
322              140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710\
323              C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597",
324         ).unwrap();
325         let prime_q = BigNum::from_hex_str(
326             "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED\
327              4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A\
328              57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5\
329              045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E\
330              052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67E\
331              B6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659",
332         ).unwrap();
333         let generator = BigNum::from_hex_str(
334             "8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3",
335         )
336         .unwrap();
337         let dh = Dh::from_params(
338             prime_p.to_owned().unwrap(),
339             generator.to_owned().unwrap(),
340             prime_q.to_owned().unwrap(),
341         )
342         .unwrap();
343         ctx.set_tmp_dh(&dh).unwrap();
344 
345         assert_eq!(dh.prime_p(), &prime_p);
346         assert_eq!(dh.prime_q().unwrap(), &prime_q);
347         assert_eq!(dh.generator(), &generator);
348     }
349 
350     #[test]
test_dh_from_pem()351     fn test_dh_from_pem() {
352         let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
353         let params = include_bytes!("../test/dhparams.pem");
354         let dh = Dh::params_from_pem(params).unwrap();
355         ctx.set_tmp_dh(&dh).unwrap();
356     }
357 
358     #[test]
test_dh_from_der()359     fn test_dh_from_der() {
360         let params = include_bytes!("../test/dhparams.pem");
361         let dh = Dh::params_from_pem(params).unwrap();
362         let der = dh.params_to_der().unwrap();
363         Dh::params_from_der(&der).unwrap();
364     }
365 
366     #[test]
367     #[cfg(ossl102)]
test_dh_generate_key_compute_key()368     fn test_dh_generate_key_compute_key() {
369         let dh1 = Dh::get_2048_224().unwrap().generate_key().unwrap();
370         let dh2 = Dh::get_2048_224().unwrap().generate_key().unwrap();
371 
372         let shared_a = dh1.compute_key(dh2.public_key()).unwrap();
373         let shared_b = dh2.compute_key(dh1.public_key()).unwrap();
374 
375         assert_eq!(shared_a, shared_b);
376     }
377 
378     #[test]
test_dh_generate_params_generate_key_compute_key()379     fn test_dh_generate_params_generate_key_compute_key() {
380         let dh_params1 = Dh::generate_params(512, 2).unwrap();
381         let dh_params2 = Dh::from_pqg(
382             dh_params1.prime_p().to_owned().unwrap(),
383             None,
384             dh_params1.generator().to_owned().unwrap(),
385         )
386         .unwrap();
387 
388         let dh1 = dh_params1.generate_key().unwrap();
389         let dh2 = dh_params2.generate_key().unwrap();
390 
391         let shared_a = dh1.compute_key(dh2.public_key()).unwrap();
392         let shared_b = dh2.compute_key(dh1.public_key()).unwrap();
393 
394         assert_eq!(shared_a, shared_b);
395     }
396 }
397