aboutsummaryrefslogtreecommitdiff
path: root/openssl
diff options
context:
space:
mode:
authorAron Wieck <[email protected]>2018-08-09 15:37:23 +0200
committerAron Wieck <[email protected]>2018-08-14 16:04:33 +0200
commit59c578cf04f96e02871c509d9c64a3d26a6467a4 (patch)
treed6f5d3551753772f96f685f6f7438b1be4f8b787 /openssl
parentMerge pull request #974 from sfackler/shutdown (diff)
downloadrust-openssl-59c578cf04f96e02871c509d9c64a3d26a6467a4.tar.xz
rust-openssl-59c578cf04f96e02871c509d9c64a3d26a6467a4.zip
Add methods for DTLS/SRTP key handshake
Diffstat (limited to 'openssl')
-rw-r--r--openssl/src/lib.rs1
-rw-r--r--openssl/src/srtp.rs54
-rw-r--r--openssl/src/ssl/mod.rs95
-rw-r--r--openssl/src/ssl/test.rs114
4 files changed, 264 insertions, 0 deletions
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index d74ce65f..e6455482 100644
--- a/openssl/src/lib.rs
+++ b/openssl/src/lib.rs
@@ -57,6 +57,7 @@ pub mod rand;
pub mod rsa;
pub mod sha;
pub mod sign;
+pub mod srtp;
pub mod ssl;
pub mod stack;
pub mod string;
diff --git a/openssl/src/srtp.rs b/openssl/src/srtp.rs
new file mode 100644
index 00000000..136ddbd2
--- /dev/null
+++ b/openssl/src/srtp.rs
@@ -0,0 +1,54 @@
+use ffi;
+use foreign_types::ForeignTypeRef;
+use libc::c_ulong;
+use stack::Stackable;
+use std::ffi::CStr;
+use std::str;
+
+#[allow(unused_unsafe)]
+foreign_type_and_impl_send_sync! {
+ type CType = ffi::SRTP_PROTECTION_PROFILE;
+ fn drop = ffi::SRTP_PROTECTION_PROFILE_free;
+
+ pub struct SrtpProtectionProfile;
+ /// Reference to `SrtpProtectionProfile`.
+ pub struct SrtpProtectionProfileRef;
+}
+
+impl Stackable for SrtpProtectionProfile {
+ type StackType = ffi::stack_st_SRTP_PROTECTION_PROFILE;
+}
+
+
+impl SrtpProtectionProfileRef {
+ pub fn id(&self) -> SrtpProfileId {
+ SrtpProfileId::from_raw(unsafe { (*self.as_ptr()).id })
+ }
+ pub fn name(&self) -> &'static str {
+ unsafe { CStr::from_ptr((*self.as_ptr()).name as *const _) }.to_str().expect("should be UTF-8")
+ }
+}
+
+
+/// type of SRTP profile to use.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct SrtpProfileId(c_ulong);
+
+impl SrtpProfileId {
+ /// Creates a `SrtpProfileId` from an integer representation.
+ pub fn from_raw(value: c_ulong) -> SrtpProfileId {
+ SrtpProfileId(value)
+ }
+
+ /// Returns the integer representation of `SrtpProfileId`.
+ pub fn as_raw(&self) -> c_ulong {
+ self.0
+ }
+
+ pub const SRTP_AES128_CM_SHA1_80: SrtpProfileId = SrtpProfileId(ffi::SRTP_AES128_CM_SHA1_80);
+ pub const SRTP_AES128_CM_SHA1_32: SrtpProfileId = SrtpProfileId(ffi::SRTP_AES128_CM_SHA1_32);
+ pub const SRTP_AES128_F8_SHA1_80: SrtpProfileId = SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_80);
+ pub const SRTP_AES128_F8_SHA1_32: SrtpProfileId = SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_32);
+ pub const SRTP_NULL_SHA1_80: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_80);
+ pub const SRTP_NULL_SHA1_32: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_32);
+}
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 7732765a..c6305dcd 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -88,6 +88,7 @@ use hash::MessageDigest;
#[cfg(ossl110)]
use nid::Nid;
use pkey::{HasPrivate, PKeyRef, Params, Private};
+use srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef};
use ssl::bio::BioMethod;
use ssl::callbacks::*;
use ssl::error::InnerError;
@@ -1156,6 +1157,28 @@ impl SslContextBuilder {
}
}
+ /// Enables the DTLS extension "use_srtp" as defined in RFC5764.
+ ///
+ /// This corresponds to [`SSL_CTX_set_tlsext_use_srtp`].
+ ///
+ /// [`SSL_CTX_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html
+ pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> {
+ unsafe {
+ let cstr = CString::new(protocols).unwrap();
+
+ let r = ffi::SSL_CTX_set_tlsext_use_srtp(
+ self.as_ptr(),
+ cstr.as_ptr(),
+ );
+ // fun fact, set_tlsext_use_srtp has a reversed return code D:
+ if r == 0 {
+ Ok(())
+ } else {
+ Err(ErrorStack::get())
+ }
+ }
+ }
+
/// Sets the callback used by a server to select a protocol for Application Layer Protocol
/// Negotiation (ALPN).
///
@@ -2455,6 +2478,78 @@ impl SslRef {
}
}
+
+ /// Enables the DTLS extension "use_srtp" as defined in RFC5764.
+ ///
+ /// This corresponds to [`SSL_set_tlsext_use_srtp`].
+ ///
+ /// [`SSL_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html
+ pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> {
+ unsafe {
+ let cstr = CString::new(protocols).unwrap();
+
+ let r = ffi::SSL_set_tlsext_use_srtp(
+ self.as_ptr(),
+ cstr.as_ptr(),
+ );
+ // fun fact, set_tlsext_use_srtp has a reversed return code D:
+ if r == 0 {
+ Ok(())
+ } else {
+ Err(ErrorStack::get())
+ }
+ }
+ }
+
+ /// Gets all SRTP profiles that are enabled for handshake via set_tlsext_use_srtp
+ ///
+ /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled.
+ ///
+ /// This corresponds to [`SSL_get_srtp_profiles`].
+ ///
+ /// [`SSL_get_srtp_profiles`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html
+ pub fn get_srtp_profiles(&self) -> Option<&StackRef<SrtpProtectionProfile>> {
+ unsafe {
+ let chain = ffi::SSL_get_srtp_profiles(self.as_ptr());
+
+ if chain.is_null() {
+ None
+ } else {
+ Some(StackRef::from_ptr(chain))
+ }
+ }
+ }
+ /// Gets the SRTP profile selected by handshake.
+ ///
+ /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled.
+ ///
+ /// This corresponds to [`SSL_get_selected_srtp_profile`].
+ ///
+ /// [`SSL_get_selected_srtp_profile`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html
+ pub fn selected_srtp_profile(&self) -> Option<&SrtpProtectionProfileRef> {
+ unsafe {
+ let profile = ffi::SSL_get_selected_srtp_profile(self.as_ptr());
+
+ if profile.is_null() {
+ None
+ } else {
+ Some(SrtpProtectionProfileRef::from_ptr(profile as *mut _))
+ }
+ }
+ }
+
+ /// Derives keying material for SRTP usage.
+ ///
+ /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled.
+ ///
+ /// This corresponds to [`SSL_export_keying_material`] with a label of "EXTRACTOR-dtls_srtp".
+ ///
+ /// [`SSL_export_keying_material`]: https://www.openssl.org/docs/manmaster/man3/SSL_export_keying_material.html
+ /// [`SSL_CTX_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html
+ pub fn export_srtp_keying_material(&self, out: &mut [u8]) -> Result<(), ErrorStack> {
+ self.export_keying_material(out, "EXTRACTOR-dtls_srtp", None)
+ }
+
/// Returns the number of bytes remaining in the currently processed TLS record.
///
/// If this is greater than 0, the next call to `read` will not call down to the underlying
diff --git a/openssl/src/ssl/test.rs b/openssl/src/ssl/test.rs
index a8d69295..ad800e08 100644
--- a/openssl/src/ssl/test.rs
+++ b/openssl/src/ssl/test.rs
@@ -21,6 +21,7 @@ use pkey::PKey;
use ssl;
#[cfg(any(ossl110, ossl111, libressl261))]
use ssl::SslVersion;
+use srtp::SrtpProfileId;
use ssl::{
Error, HandshakeError, MidHandshakeSslStream, ShutdownResult, ShutdownState, Ssl, SslAcceptor,
SslConnector, SslContext, SslFiletype, SslMethod, SslSessionCacheMode, SslStream,
@@ -546,6 +547,119 @@ fn test_connect_with_alpn_successful_single_match() {
assert_eq!(b"spdy/3.1", stream.ssl().selected_alpn_protocol().unwrap());
}
+/// Tests that when both the client as well as the server use SRTP and their
+/// lists of supported protocols have an overlap -- with only ONE protocol
+/// being valid for both.
+#[test]
+fn test_connect_with_srtp_ctx() {
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+
+
+ let guard = thread::spawn(move || {
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32").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 mut stream = ssl.accept(stream).unwrap();
+
+ let mut buf = [0; 60];
+ stream
+ .ssl()
+ .export_srtp_keying_material(&mut buf)
+ .unwrap();
+
+ stream.write_all(&[0]).unwrap();
+
+ buf
+ });
+
+ let stream = TcpStream::connect(addr).unwrap();
+ let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32").unwrap();
+ let ssl = Ssl::new(&ctx.build()).unwrap();
+ let mut stream = ssl.connect(stream).unwrap();
+
+ let mut buf = [1; 60];
+ {
+ let srtp_profile = stream.ssl().selected_srtp_profile().unwrap();
+ assert_eq!("SRTP_AES128_CM_SHA1_80", srtp_profile.name());
+ assert_eq!(SrtpProfileId::SRTP_AES128_CM_SHA1_80, srtp_profile.id());
+ }
+ stream.ssl().export_srtp_keying_material(&mut buf).expect("extract");
+
+ stream.read_exact(&mut [0]).unwrap();
+
+ let buf2 = guard.join().unwrap();
+
+ assert_eq!(buf[..], buf2[..]);
+}
+
+/// Tests that when both the client as well as the server use SRTP and their
+/// lists of supported protocols have an overlap -- with only ONE protocol
+/// being valid for both.
+#[test]
+fn test_connect_with_srtp_ssl() {
+ let listener = TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = listener.local_addr().unwrap();
+
+
+ let guard = thread::spawn(move || {
+ let stream = listener.accept().unwrap().0;
+ let mut ctx = SslContext::builder(SslMethod::dtls()).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 mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32").unwrap();
+ let mut profilenames = String::new();
+ for profile in ssl.get_srtp_profiles().unwrap() {
+ if profilenames.len()>0 {
+ profilenames.push(':');
+ }
+ profilenames += profile.name();
+
+ }
+ assert_eq!("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32", profilenames);
+ let mut stream = ssl.accept(stream).unwrap();
+
+ let mut buf = [0; 60];
+ stream
+ .ssl()
+ .export_srtp_keying_material(&mut buf)
+ .unwrap();
+
+ stream.write_all(&[0]).unwrap();
+
+ buf
+ });
+
+ let stream = TcpStream::connect(addr).unwrap();
+ let ctx = SslContext::builder(SslMethod::dtls()).unwrap();
+ let mut ssl = Ssl::new(&ctx.build()).unwrap();
+ ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32").unwrap();
+ let mut stream = ssl.connect(stream).unwrap();
+
+ let mut buf = [1; 60];
+ {
+ let srtp_profile = stream.ssl().selected_srtp_profile().unwrap();
+ assert_eq!("SRTP_AES128_CM_SHA1_80", srtp_profile.name());
+ assert_eq!(SrtpProfileId::SRTP_AES128_CM_SHA1_80, srtp_profile.id());
+ }
+ stream.ssl().export_srtp_keying_material(&mut buf).expect("extract");
+
+ stream.read_exact(&mut [0]).unwrap();
+
+ let buf2 = guard.join().unwrap();
+
+ assert_eq!(buf[..], buf2[..]);
+}
+
/// Tests that when the `SslStream` is created as a server stream, the protocols
/// are correctly advertised to the client.
#[test]