diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 3890ea96..2b297143 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -2406,6 +2406,16 @@ extern "C" { ctx: *mut SSL, dh: unsafe extern "C" fn(ssl: *mut SSL, is_export: c_int, keylength: c_int) -> *mut DH, ); + pub fn SSL_export_keying_material( + s: *mut SSL, + out: *mut c_uchar, + olen: size_t, + label: *const c_char, + llen: size_t, + context: *const c_uchar, + contextlen: size_t, + use_context: c_int, + ) -> c_int; #[cfg(not(any(osslconf = "OPENSSL_NO_COMP", libressl)))] pub fn SSL_COMP_get_name(comp: *const COMP_METHOD) -> *const c_char; diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index e4796df0..fb7db988 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -59,8 +59,7 @@ //! ``` use ffi; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; -use libc::{c_int, c_long, c_ulong, c_void}; -use libc::{c_uchar, c_uint}; +use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong, c_void}; use std::any::TypeId; use std::cmp; use std::collections::HashMap; @@ -2141,6 +2140,35 @@ impl SslRef { } } + /// Derives keying material for application use in accordance to RFC 5705. + /// + /// This corresponds to [`SSL_export_keying_material`]. + /// + /// [`SSL_export_keying_material`]: https://www.openssl.org/docs/manmaster/man3/SSL_export_keying_material.html + pub fn export_keying_material( + &self, + out: &mut [u8], + label: &str, + context: Option<&[u8]>, + ) -> Result<(), ErrorStack> { + unsafe { + let (context, contextlen, use_context) = match context { + Some(context) => (context.as_ptr() as *const c_uchar, context.len(), 1), + None => (ptr::null(), 0, 0), + }; + cvt(ffi::SSL_export_keying_material( + self.as_ptr(), + out.as_mut_ptr() as *mut c_uchar, + out.len(), + label.as_ptr() as *const c_char, + label.len(), + context, + contextlen, + use_context, + )).map(|_| ()) + } + } + /// Sets the session to be used. /// /// This should be called before the handshake to attempt to reuse a previously established diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs index 1913d835..b8e9b8c2 100644 --- a/openssl/src/ssl/test.rs +++ b/openssl/src/ssl/test.rs @@ -1278,6 +1278,48 @@ fn new_session_callback() { assert!(CALLED_BACK.load(Ordering::SeqCst)); } +#[test] +fn keying_export() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let label = "EXPERIMENTAL test"; + let context = b"my context"; + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + let stream = ssl.accept(stream).unwrap(); + + let mut buf = [0; 32]; + stream + .ssl() + .export_keying_material(&mut buf, label, Some(context)) + .unwrap(); + buf + }); + + let stream = TcpStream::connect(addr).unwrap(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + let stream = ssl.connect(stream).unwrap(); + + let mut buf = [1; 32]; + stream + .ssl() + .export_keying_material(&mut buf, label, Some(context)) + .unwrap(); + + let buf2 = guard.join().unwrap(); + + assert_eq!(buf, buf2); +} + fn _check_kinds() { fn is_send() {} fn is_sync() {}