diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index cbdfc6309..a4575d0c9 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -614,7 +614,7 @@ impl From for ExtensionType { } } -/// An SSL/TLS protocol version. +/// An SSL/TLS/DTLS protocol version. #[derive(Copy, Clone, PartialEq, Eq)] pub struct SslVersion(u16); @@ -633,6 +633,15 @@ impl SslVersion { /// TLSv1.3 pub const TLS1_3: SslVersion = SslVersion(ffi::TLS1_3_VERSION as _); + + /// DTLSv1.0 + pub const DTLS1: SslVersion = SslVersion(ffi::DTLS1_VERSION as _); + + /// DTLSv1.2 + pub const DTLS1_2: SslVersion = SslVersion(ffi::DTLS1_2_VERSION as _); + + /// DTLSv1.3 + pub const DTLS1_3: SslVersion = SslVersion(ffi::DTLS1_3_VERSION as _); } impl TryFrom for SslVersion { @@ -644,7 +653,10 @@ impl TryFrom for SslVersion { | ffi::TLS1_VERSION | ffi::TLS1_1_VERSION | ffi::TLS1_2_VERSION - | ffi::TLS1_3_VERSION => Ok(Self(value)), + | ffi::TLS1_3_VERSION + | ffi::DTLS1_VERSION + | ffi::DTLS1_2_VERSION + | ffi::DTLS1_3_VERSION => Ok(Self(value)), _ => Err("Unknown SslVersion"), } } @@ -658,6 +670,9 @@ impl fmt::Debug for SslVersion { Self::TLS1_1 => "TLS1_1", Self::TLS1_2 => "TLS1_2", Self::TLS1_3 => "TLS1_3", + Self::DTLS1 => "DTLS1", + Self::DTLS1_2 => "DTLS1_2", + Self::DTLS1_3 => "DTLS1_3", _ => return write!(f, "{:#06x}", self.0), }) } @@ -671,6 +686,9 @@ impl fmt::Display for SslVersion { Self::TLS1_1 => "TLSv1.1", Self::TLS1_2 => "TLSv1.2", Self::TLS1_3 => "TLSv1.3", + Self::DTLS1 => "DTLSv1.0", + Self::DTLS1_2 => "DTLSv1.2", + Self::DTLS1_3 => "DTLSv1.3", _ => return write!(f, "unknown ({:#06x})", self.0), }) } diff --git a/boring/src/ssl/test/mod.rs b/boring/src/ssl/test/mod.rs index fa3438aba..0159aa392 100644 --- a/boring/src/ssl/test/mod.rs +++ b/boring/src/ssl/test/mod.rs @@ -233,6 +233,53 @@ fn test_connect_with_srtp_ssl() { assert_eq!(buf[..], buf2[..]); } +/// Tests that DTLS 1.3 can be enabled and negotiated successfully. +#[test] +fn test_dtls_1_3_version() { + 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(); + // Enable DTLS 1.3 + ctx.set_max_proto_version(Some(SslVersion::DTLS1_3)) + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_mtu(1500).unwrap(); + let stream = ssl.accept(stream).unwrap(); + + // Verify DTLS 1.3 was negotiated + let version = stream.ssl().version2().unwrap(); + assert_eq!(version, SslVersion::DTLS1_3); + + stream + }); + + let stream = TcpStream::connect(addr).unwrap(); + let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + // Enable DTLS 1.3 on client + ctx.set_max_proto_version(Some(SslVersion::DTLS1_3)) + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_mtu(1500).unwrap(); + let stream = ssl.connect(stream).unwrap(); + + // Verify DTLS 1.3 was negotiated on client side + let version = stream.ssl().version2().unwrap(); + assert_eq!(version, SslVersion::DTLS1_3); + + // Also check version string + let version_str = stream.ssl().version_str(); + assert_eq!(version_str, "DTLSv1.3"); + + guard.join().unwrap(); +} + /// Tests that when the `SslStream` is created as a server stream, the protocols /// are correctly advertised to the client. #[test]