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