From d5d414b16fe13d65938acd6c601445e1b3e02b55 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Thu, 17 May 2018 03:23:30 -0700 Subject: [PATCH 1/3] Expose max TLS1.3 early data accessors --- openssl-sys/src/openssl/v111.rs | 7 ++++ openssl/src/ssl/mod.rs | 70 +++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/openssl-sys/src/openssl/v111.rs b/openssl-sys/src/openssl/v111.rs index 8574efc8..e284ff98 100644 --- a/openssl-sys/src/openssl/v111.rs +++ b/openssl-sys/src/openssl/v111.rs @@ -82,4 +82,11 @@ extern "C" { cookie_len: size_t ) -> c_int> ); + + pub fn SSL_CTX_set_max_early_data(ctx: *mut ::SSL_CTX, max_early_data: u32) -> c_int; + pub fn SSL_CTX_get_max_early_data(ctx: *const ::SSL_CTX) -> u32; + pub fn SSL_set_max_early_data(ctx: *mut ::SSL, max_early_data: u32) -> c_int; + pub fn SSL_get_max_early_data(ctx: *const ::SSL) -> u32; + pub fn SSL_SESSION_set_max_early_data(ctx: *mut ::SSL_SESSION, max_early_data: u32) -> c_int; + pub fn SSL_SESSION_get_max_early_data(ctx: *const ::SSL_SESSION) -> u32; } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 8dc605ed..33ffa737 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1489,6 +1489,24 @@ impl SslContextBuilder { } } + /// Sets the maximum amount of early data that will be accepted on incoming connections. + /// + /// Defaults to 0. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_CTX_set_max_early_data`]. + /// + /// [`SSL_CTX_set_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_max_early_data.html + #[cfg(ossl111)] + pub fn set_max_early_data(&mut self, bytes: u32) -> Result<(), ErrorStack> { + if unsafe { ffi::SSL_CTX_set_max_early_data(self.as_ptr(), bytes) } == 1 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + /// Consumes the builder, returning a new `SslContext`. pub fn build(self) -> SslContext { self.0 @@ -1643,6 +1661,18 @@ impl SslContextRef { } } } + + /// Gets the maximum amount of early data that will be accepted on incoming connections. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_CTX_get_max_early_data`]. + /// + /// [`SSL_CTX_get_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_get_max_early_data.html + #[cfg(ossl111)] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_CTX_get_max_early_data(self.as_ptr()) } + } } /// Information about the state of a cipher. @@ -1873,6 +1903,18 @@ impl SslSessionRef { unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } } + /// Gets the maximum amount of early data that can be sent on this session. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_SESSION_get_max_early_data`]. + /// + /// [`SSL_SESSION_get_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_max_early_data.html + #[cfg(ossl111)] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_SESSION_get_max_early_data(self.as_ptr()) } + } + to_der! { /// Serializes the session into a DER-encoded structure. /// @@ -2594,6 +2636,34 @@ impl SslRef { } } } + + /// Sets the maximum amount of early data that will be accepted on this connection. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_set_max_early_data`]. + /// + /// [`SSL_set_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_set_max_early_data.html + #[cfg(ossl111)] + pub fn set_max_early_data(&mut self, bytes: u32) -> Result<(), ErrorStack> { + if unsafe { ffi::SSL_set_max_early_data(self.as_ptr(), bytes) } == 1 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + + /// Gets the maximum amount of early data that can be sent on this connection. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_get_max_early_data`]. + /// + /// [`SSL_get_max_early_data`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_max_early_data.html + #[cfg(ossl111)] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_get_max_early_data(self.as_ptr()) } + } } /// An SSL stream midway through the handshake process. From 69c75a178bbc70dd10d0d69ac8bf9e842cf4ff1f Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Thu, 17 May 2018 13:16:41 -0700 Subject: [PATCH 2/3] Expose early keying material export --- openssl-sys/src/openssl/v111.rs | 10 ++++++++++ openssl/src/ssl/mod.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/openssl-sys/src/openssl/v111.rs b/openssl-sys/src/openssl/v111.rs index e284ff98..4a3f9560 100644 --- a/openssl-sys/src/openssl/v111.rs +++ b/openssl-sys/src/openssl/v111.rs @@ -89,4 +89,14 @@ extern "C" { pub fn SSL_get_max_early_data(ctx: *const ::SSL) -> u32; pub fn SSL_SESSION_set_max_early_data(ctx: *mut ::SSL_SESSION, max_early_data: u32) -> c_int; pub fn SSL_SESSION_get_max_early_data(ctx: *const ::SSL_SESSION) -> u32; + + pub fn SSL_export_keying_material_early( + s: *mut ::SSL, + out: *mut c_uchar, + olen: size_t, + label: *const c_char, + llen: size_t, + context: *const c_uchar, + contextlen: size_t, + ) -> c_int; } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 33ffa737..78ae2267 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -2498,6 +2498,33 @@ impl SslRef { } } + /// Derives keying material for application use in accordance to RFC 5705. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_export_keying_material_early`]. + /// + /// [`SSL_export_keying_material_early`]: https://www.openssl.org/docs/manmaster/man3/SSL_export_keying_material_early.html + #[cfg(ossl111)] + pub fn export_keying_material_early( + &self, + out: &mut [u8], + label: &str, + context: &[u8], + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_export_keying_material_early( + self.as_ptr(), + out.as_mut_ptr() as *mut c_uchar, + out.len(), + label.as_ptr() as *const c_char, + label.len(), + context.as_ptr() as *const c_uchar, + context.len(), + )).map(|_| ()) + } + } + /// Sets the session to be used. /// /// This should be called before the handshake to attempt to reuse a previously established From 2e478fdcf47bcb69a098bd6c3ab6227b53d45a24 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Thu, 17 May 2018 15:17:40 -0700 Subject: [PATCH 3/3] Expose early I/O --- openssl-sys/src/lib.rs | 3 ++ openssl-sys/src/openssl/v111.rs | 6 +++ openssl/src/ssl/mod.rs | 90 ++++++++++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 61e087d0..a48f6a85 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -2832,4 +2832,7 @@ extern "C" { pub fn EVP_MD_size(md: *const EVP_MD) -> c_int; pub fn EVP_get_cipherbyname(name: *const c_char) -> *const EVP_CIPHER; + + pub fn SSL_set_connect_state(s: *mut SSL); + pub fn SSL_set_accept_state(s: *mut SSL); } diff --git a/openssl-sys/src/openssl/v111.rs b/openssl-sys/src/openssl/v111.rs index 4a3f9560..36682663 100644 --- a/openssl-sys/src/openssl/v111.rs +++ b/openssl-sys/src/openssl/v111.rs @@ -55,6 +55,9 @@ pub const SSL_EXT_TLS1_3_CERTIFICATE: c_uint = 0x1000; pub const SSL_EXT_TLS1_3_NEW_SESSION_TICKET: c_uint = 0x2000; pub const SSL_EXT_TLS1_3_CERTIFICATE_REQUEST: c_uint = 0x4000; +pub const SSL_READ_EARLY_DATA_ERROR: c_int = 0; +pub const SSL_READ_EARLY_DATA_SUCCESS: c_int = 1; +pub const SSL_READ_EARLY_DATA_FINISH: c_int = 2; extern "C" { pub fn SSL_CTX_set_keylog_callback(ctx: *mut ::SSL_CTX, cb: SSL_CTX_keylog_cb_func); @@ -99,4 +102,7 @@ extern "C" { context: *const c_uchar, contextlen: size_t, ) -> c_int; + + pub fn SSL_write_early_data(s: *mut ::SSL, buf: *const c_void, num: size_t, written: *mut size_t) -> c_int; + pub fn SSL_read_early_data(s: *mut ::SSL, buf: *mut c_void, num: size_t, readbytes: *mut size_t) -> c_int; } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 78ae2267..623db801 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -2500,6 +2500,9 @@ impl SslRef { /// Derives keying material for application use in accordance to RFC 5705. /// + /// This function is only usable with TLSv1.3, wherein there is no distinction between an empty context and no + /// context. Therefore, unlike `export_keying_material`, `context` must always be supplied. + /// /// Requires OpenSSL 1.1.1 or newer. /// /// This corresponds to [`SSL_export_keying_material_early`]. @@ -3001,6 +3004,24 @@ where } } + /// Configure as an outgoing stream from a client. + /// + /// This corresponds to [`SSL_set_connect_state`]. + /// + /// [`SSL_set_connect_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_connect_state.html + pub fn set_connect_state(&mut self) { + unsafe { ffi::SSL_set_connect_state(self.inner.ssl.as_ptr()) } + } + + /// Configure as an incoming stream to a server. + /// + /// This corresponds to [`SSL_set_accept_state`]. + /// + /// [`SSL_set_accept_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_accept_state.html + pub fn set_accept_state(&mut self) { + unsafe { ffi::SSL_set_accept_state(self.inner.ssl.as_ptr()) } + } + /// See `Ssl::connect` pub fn connect(self) -> Result, HandshakeError> { let mut stream = self.inner; @@ -3041,7 +3062,74 @@ where } } - // Future work: early IO methods + /// Initiates the handshake. + /// + /// This will fail if `set_accept_state` or `set_connect_state` was not called first. + /// + /// This corresponds to [`SSL_do_handshake`]. + /// + /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + pub fn handshake(self) -> Result, HandshakeError> { + let mut stream = self.inner; + let ret = unsafe { ffi::SSL_do_handshake(stream.ssl.as_ptr()) }; + if ret > 0 { + Ok(stream) + } else { + let error = stream.make_error(ret); + match error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + Err(HandshakeError::WouldBlock(MidHandshakeSslStream { stream, error })) + } + _ => Err(HandshakeError::Failure(MidHandshakeSslStream { stream, error })), + } + } + } + + /// Read application data transmitted by a client before handshake + /// completion. + /// + /// Useful for reducing latency, but vulnerable to replay attacks. Call + /// `set_accept_state` first. + /// + /// Returns `Ok(0)` if all early data has been read. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_read_early_data`]. + /// + /// [`SSL_read_early_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_read_early_data.html + #[cfg(ossl111)] + pub fn read_early_data(&mut self, buf: &mut [u8]) -> Result { + let mut read = 0; + let ret = unsafe { ffi::SSL_read_early_data(self.inner.ssl.as_ptr(), buf.as_ptr() as *mut c_void, buf.len(), &mut read) }; + match ret { + ffi::SSL_READ_EARLY_DATA_ERROR => Err(self.inner.make_error(ret)), + ffi::SSL_READ_EARLY_DATA_SUCCESS => Ok(read), + ffi::SSL_READ_EARLY_DATA_FINISH => Ok(0), + _ => unreachable!(), + } + } + + /// Send data to the server without blocking on handshake completion. + /// + /// Useful for reducing latency, but vulnerable to replay attacks. Call + /// `set_connect_state` first. + /// + /// Requires OpenSSL 1.1.1 or newer. + /// + /// This corresponds to [`SSL_write_early_data`]. + /// + /// [`SSL_write_early_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_write_early_data.html + #[cfg(ossl111)] + pub fn write_early_data(&mut self, buf: &[u8]) -> Result { + let mut written = 0; + let ret = unsafe { ffi::SSL_write_early_data(self.inner.ssl.as_ptr(), buf.as_ptr() as *const c_void, buf.len(), &mut written) }; + if ret > 0 { + Ok(written as usize) + } else { + Err(self.inner.make_error(ret)) + } + } } impl SslStreamBuilder {