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