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