From 7f2cdcd96b53a58c3e828cf4868f9965604bc107 Mon Sep 17 00:00:00 2001 From: Ivan Nikulin Date: Wed, 28 Apr 2021 14:26:58 +0100 Subject: [PATCH] MAL-359 Add RPK support to tokio-boring Assertions were added to the methods that can't be used in RPK mode. Without those we would hit assertions in BoringSSL code which causes SIGTERM on FFI boundary. --- boring/src/ssl/connector.rs | 45 +++++++-- boring/src/ssl/mod.rs | 164 ++++++++++++++++++++++++++++++--- tokio-boring/tests/google.rs | 101 ++++++++++++++++++-- tokio-boring/tests/pubkey.der | Bin 0 -> 294 bytes tokio-boring/tests/pubkey2.der | Bin 0 -> 294 bytes 5 files changed, 282 insertions(+), 28 deletions(-) create mode 100644 tokio-boring/tests/pubkey.der create mode 100644 tokio-boring/tests/pubkey2.der diff --git a/boring/src/ssl/connector.rs b/boring/src/ssl/connector.rs index 12e486c89..ba8ebfd19 100644 --- a/boring/src/ssl/connector.rs +++ b/boring/src/ssl/connector.rs @@ -20,9 +20,17 @@ ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== -----END DH PARAMETERS----- "; +enum ContextType { + WithMethod(SslMethod), + Rpk, +} + #[allow(clippy::inconsistent_digit_grouping)] -fn ctx(method: SslMethod) -> Result { - let mut ctx = SslContextBuilder::new(method)?; +fn ctx(ty: ContextType) -> Result { + let mut ctx = match ty { + ContextType::WithMethod(method) => SslContextBuilder::new(method), + ContextType::Rpk => SslContextBuilder::new_rpk(), + }?; let mut opts = SslOptions::ALL | SslOptions::NO_COMPRESSION @@ -64,7 +72,7 @@ impl SslConnector { /// /// The default configuration is subject to change, and is currently derived from Python. pub fn builder(method: SslMethod) -> Result { - let mut ctx = ctx(method)?; + let mut ctx = ctx(ContextType::WithMethod(method))?; ctx.set_default_verify_paths()?; ctx.set_cipher_list( "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", @@ -74,6 +82,16 @@ impl SslConnector { Ok(SslConnectorBuilder(ctx)) } + /// Creates a new builder for TLS connections with raw public key. + pub fn rpk_builder() -> Result { + let mut ctx = ctx(ContextType::Rpk)?; + ctx.set_cipher_list( + "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", + )?; + + Ok(SslConnectorBuilder(ctx)) + } + /// Initiates a client-side TLS session on a stream. /// /// The domain is used for SNI and hostname verification. @@ -179,7 +197,7 @@ impl ConnectConfiguration { self.ssl.set_hostname(domain)?; } - if self.verify_hostname { + if !self.ssl.ssl_context().is_rpk() && self.verify_hostname { setup_verify_hostname(&mut self.ssl, domain)?; } @@ -209,6 +227,19 @@ impl DerefMut for ConnectConfiguration { pub struct SslAcceptor(SslContext); impl SslAcceptor { + pub fn rpk() -> Result { + let mut ctx = ctx(ContextType::Rpk)?; + ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); + let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; + ctx.set_tmp_dh(&dh)?; + ctx.set_cipher_list( + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ + ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ + DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384" + )?; + Ok(SslAcceptorBuilder(ctx)) + } + /// Creates a new builder configured to connect to non-legacy clients. This should generally be /// considered a reasonable default choice. /// @@ -217,7 +248,7 @@ impl SslAcceptor { /// /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS pub fn mozilla_intermediate_v5(method: SslMethod) -> Result { - let mut ctx = ctx(method)?; + let mut ctx = ctx(ContextType::WithMethod(method))?; ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; ctx.set_tmp_dh(&dh)?; @@ -238,7 +269,7 @@ impl SslAcceptor { /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS // FIXME remove in next major version pub fn mozilla_intermediate(method: SslMethod) -> Result { - let mut ctx = ctx(method)?; + let mut ctx = ctx(ContextType::WithMethod(method))?; ctx.set_options(SslOptions::CIPHER_SERVER_PREFERENCE); ctx.set_options(SslOptions::NO_TLSV1_3); let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; @@ -264,7 +295,7 @@ impl SslAcceptor { /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS // FIXME remove in next major version pub fn mozilla_modern(method: SslMethod) -> Result { - let mut ctx = ctx(method)?; + let mut ctx = ctx(ContextType::WithMethod(method))?; ctx.set_options( SslOptions::CIPHER_SERVER_PREFERENCE | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1, ); diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index a3bbc48f9..e0b87429e 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -239,6 +239,11 @@ impl SslMethod { unsafe { SslMethod(TLS_method()) } } + /// Same as `tls`, but doesn't create X509 for certificates. + pub fn tls_with_buffer() -> SslMethod { + unsafe { SslMethod(TLS_with_buffers_method()) } + } + /// Support all versions of the DTLS protocol. /// /// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method` @@ -411,6 +416,7 @@ lazy_static! { static ref INDEXES: Mutex> = Mutex::new(HashMap::new()); static ref SSL_INDEXES: Mutex> = Mutex::new(HashMap::new()); static ref SESSION_CTX_INDEX: Index = Ssl::new_ex_index().unwrap(); + static ref RPK_FLAG_INDEX: Index = SslContext::new_ex_index().unwrap(); } unsafe extern "C" fn free_data_box( @@ -634,8 +640,19 @@ pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8] } } +extern "C" fn rpk_verify_failure_callback( + _ssl: *mut ffi::SSL, + _out_alert: *mut u8, +) -> ffi::ssl_verify_result_t { + // Always verify the peer. + ffi::ssl_verify_result_t::ssl_verify_invalid +} + /// A builder for `SslContext`s. -pub struct SslContextBuilder(SslContext); +pub struct SslContextBuilder { + ctx: SslContext, + is_rpk: bool, +} impl SslContextBuilder { /// Creates a new `SslContextBuilder`. @@ -648,7 +665,48 @@ impl SslContextBuilder { init(); let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?; - Ok(SslContextBuilder::from_ptr(ctx)) + Ok(SslContextBuilder::from_ptr(ctx, false)) + } + } + + /// Creates a new `SslContextBuilder` to be used with Raw Public Key. + /// + /// This corresponds to [`SSL_CTX_new`]. + /// + /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html + pub fn new_rpk() -> Result { + unsafe { + init(); + let ctx = cvt_p(ffi::SSL_CTX_new(SslMethod::tls_with_buffer().as_ptr()))?; + + Ok(SslContextBuilder::from_ptr(ctx, true)) + } + } + + /// Sets raw public key certificate in DER format. + pub fn set_rpk_certificate(&mut self, cert: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_CTX_set_server_raw_public_key_certificate( + self.as_ptr(), + cert.as_ptr(), + cert.len() as u32, + )) + .map(|_| ()) + } + } + + /// Sets RPK null chain private key. + pub fn set_null_chain_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { + cvt(ffi::SSL_CTX_set_nullchain_and_key( + self.as_ptr(), + key.as_ptr(), + ptr::null_mut(), + )) + .map(|_| ()) } } @@ -657,13 +715,19 @@ impl SslContextBuilder { /// # Safety /// /// The caller must ensure that the pointer is valid and uniquely owned by the builder. - pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder { - SslContextBuilder(SslContext::from_ptr(ctx)) + pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX, is_rpk: bool) -> SslContextBuilder { + let ctx = SslContext::from_ptr(ctx); + + let mut builder = SslContextBuilder { ctx, is_rpk }; + + builder.set_ex_data(*RPK_FLAG_INDEX, is_rpk); + + builder } /// Returns a pointer to the raw OpenSSL value. pub fn as_ptr(&self) -> *mut ffi::SSL_CTX { - self.0.as_ptr() + self.ctx.as_ptr() } /// Configures the certificate verification method for new connections. @@ -672,6 +736,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html pub fn set_verify(&mut self, mode: SslVerifyMode) { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, None); } @@ -691,6 +757,8 @@ impl SslContextBuilder { where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { self.set_ex_data(SslContext::cached_ex_index::(), verify); ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::)); @@ -735,6 +803,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_verify_depth`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify_depth.html pub fn set_verify_depth(&mut self, depth: u32) { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int); } @@ -746,6 +816,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set0_verify_cert_store`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set0_verify_cert_store.html pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { let ptr = cert_store.as_ptr(); cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?; @@ -761,6 +833,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_cert_store`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_cert_store.html pub fn set_cert_store(&mut self, cert_store: X509Store) { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr()); mem::forget(cert_store); @@ -820,6 +894,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_default_verify_paths`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_default_verify_paths.html pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) } } @@ -831,6 +907,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_load_verify_locations`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_load_verify_locations.html pub fn set_ca_file>(&mut self, file: P) -> Result<(), ErrorStack> { + assert!(!self.is_rpk, "This API is not supported for RPK"); + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); unsafe { cvt(ffi::SSL_CTX_load_verify_locations( @@ -851,6 +929,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_client_CA_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_client_CA_list.html pub fn set_client_ca_list(&mut self, list: Stack) { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr()); mem::forget(list); @@ -864,6 +944,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_add_client_CA`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_client_CA_list.html pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { cvt(ffi::SSL_CTX_add_client_CA(self.as_ptr(), cacert.as_ptr())).map(|_| ()) } } @@ -905,6 +987,8 @@ impl SslContextBuilder { file: P, file_type: SslFiletype, ) -> Result<(), ErrorStack> { + assert!(!self.is_rpk, "This API is not supported for RPK"); + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); unsafe { cvt(ffi::SSL_CTX_use_certificate_file( @@ -959,6 +1043,8 @@ impl SslContextBuilder { /// /// [`SSL_CTX_add_extra_chain_cert`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { + assert!(!self.is_rpk, "This API is not supported for RPK"); + unsafe { cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?; mem::forget(cert); @@ -1236,6 +1322,7 @@ impl SslContextBuilder { /// /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html pub fn cert_store(&self) -> &X509StoreBuilderRef { + assert!(!self.is_rpk, "This API is not supported for RPK"); unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } } @@ -1245,6 +1332,7 @@ impl SslContextBuilder { /// /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef { + assert!(!self.is_rpk, "This API is not supported for RPK"); unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } } @@ -1546,7 +1634,7 @@ impl SslContextBuilder { /// Consumes the builder, returning a new `SslContext`. pub fn build(self) -> SslContext { - self.0 + self.ctx } } @@ -1630,6 +1718,7 @@ impl SslContextRef { /// /// [`SSL_CTX_get0_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html pub fn certificate(&self) -> Option<&X509Ref> { + assert!(!self.is_rpk(), "This API is not supported for RPK"); unsafe { let ptr = ffi::SSL_CTX_get0_certificate(self.as_ptr()); if ptr.is_null() { @@ -1662,6 +1751,7 @@ impl SslContextRef { /// /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html pub fn cert_store(&self) -> &X509StoreRef { + assert!(!self.is_rpk(), "This API is not supported for RPK"); unsafe { X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } } @@ -1744,9 +1834,16 @@ impl SslContextRef { /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify /// [`SSL_CTX_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_get_verify_mode.html pub fn verify_mode(&self) -> SslVerifyMode { + assert!(!self.is_rpk(), "This API is not supported for RPK"); + let mode = unsafe { ffi::SSL_CTX_get_verify_mode(self.as_ptr()) }; SslVerifyMode::from_bits(mode).expect("SSL_CTX_get_verify_mode returned invalid mode") } + + /// Returns `true` if context was created for Raw Public Key verification + pub fn is_rpk(&self) -> bool { + self.ex_data(*RPK_FLAG_INDEX).copied().unwrap_or_default() + } } /// Information about the state of a cipher. @@ -2142,16 +2239,33 @@ impl Ssl { where S: Read + Write, { + let ctx = self.ssl_context(); + + if ctx.is_rpk() { + unsafe { + ffi::SSL_CTX_set_custom_verify( + ctx.as_ptr(), + SslVerifyMode::PEER.bits(), + Some(rpk_verify_failure_callback), + ); + } + } + SslStreamBuilder::new(self, stream).accept() } } impl fmt::Debug for SslRef { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("Ssl") - .field("state", &self.state_string_long()) - .field("verify_result", &self.verify_result()) - .finish() + let mut builder = fmt.debug_struct("Ssl"); + + builder.field("state", &self.state_string_long()); + + if !self.ssl_context().is_rpk() { + builder.field("verify_result", &self.verify_result()); + } + + builder.finish() } } @@ -2181,6 +2295,10 @@ impl SslRef { /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify /// [`SSL_set_verify`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html pub fn set_verify(&mut self, mode: SslVerifyMode) { + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, None) } } @@ -2204,6 +2322,10 @@ impl SslRef { where F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, { + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); unsafe { // this needs to be in an Arc since the callback can register a new callback! self.set_ex_data(Ssl::cached_ex_index(), Arc::new(verify)); @@ -2319,6 +2441,10 @@ impl SslRef { /// /// [`SSL_get_peer_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_certificate.html pub fn peer_certificate(&self) -> Option { + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); unsafe { let ptr = ffi::SSL_get_peer_certificate(self.as_ptr()); if ptr.is_null() { @@ -2338,6 +2464,10 @@ impl SslRef { /// /// [`SSL_get_peer_cert_chain`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_cert_chain.html pub fn peer_cert_chain(&self) -> Option<&StackRef> { + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); unsafe { let ptr = ffi::SSL_get_peer_cert_chain(self.as_ptr()); if ptr.is_null() { @@ -2354,6 +2484,10 @@ impl SslRef { /// /// [`SslContext::certificate`]: struct.SslContext.html#method.certificate pub fn certificate(&self) -> Option<&X509Ref> { + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); unsafe { let ptr = ffi::SSL_get_certificate(self.as_ptr()); if ptr.is_null() { @@ -2577,6 +2711,10 @@ impl SslRef { /// /// [`SSL_get0_param`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get0_param.html pub fn param_mut(&mut self) -> &mut X509VerifyParamRef { + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) } } @@ -2586,6 +2724,10 @@ impl SslRef { /// /// [`SSL_get_verify_result`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html pub fn verify_result(&self) -> X509VerifyResult { + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); unsafe { X509VerifyResult::from_raw(ffi::SSL_get_verify_result(self.as_ptr()) as c_int) } } @@ -3334,7 +3476,7 @@ bitflags! { use ffi::{SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server}; -use ffi::{DTLS_method, TLS_client_method, TLS_method, TLS_server_method}; +use ffi::{DTLS_method, TLS_client_method, TLS_method, TLS_server_method, TLS_with_buffers_method}; use std::sync::Once; diff --git a/tokio-boring/tests/google.rs b/tokio-boring/tests/google.rs index 72c5a040c..b5e59495e 100644 --- a/tokio-boring/tests/google.rs +++ b/tokio-boring/tests/google.rs @@ -1,3 +1,4 @@ +use boring::pkey::PKey; use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; use futures::future; use std::future::Future; @@ -33,7 +34,9 @@ async fn google() { assert!(response.ends_with("") || response.ends_with("")); } -fn create_server() -> ( +fn create_server( + rpk: bool, +) -> ( impl Future, HandshakeError>>, SocketAddr, ) { @@ -45,13 +48,27 @@ fn create_server() -> ( let addr = listener.local_addr().unwrap(); let server = async move { - let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); - acceptor - .set_private_key_file("tests/key.pem", SslFiletype::PEM) - .unwrap(); - acceptor - .set_certificate_chain_file("tests/cert.pem") - .unwrap(); + let acceptor = if rpk { + let mut acceptor = SslAcceptor::rpk().unwrap(); + let pkey = std::fs::read("tests/key.pem").unwrap(); + let pkey = PKey::private_key_from_pem(&pkey).unwrap(); + let cert = std::fs::read("tests/pubkey.der").unwrap(); + + acceptor.set_rpk_certificate(&cert).unwrap(); + acceptor.set_null_chain_private_key(&pkey).unwrap(); + + acceptor + } else { + let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + acceptor + .set_private_key_file("tests/key.pem", SslFiletype::PEM) + .unwrap(); + acceptor + .set_certificate_chain_file("tests/cert.pem") + .unwrap(); + + acceptor + }; let acceptor = acceptor.build(); let stream = listener.accept().await.unwrap().0; @@ -64,7 +81,7 @@ fn create_server() -> ( #[tokio::test] async fn server() { - let (stream, addr) = create_server(); + let (stream, addr) = create_server(false); let server = async { let mut stream = stream.await.unwrap(); @@ -99,9 +116,73 @@ async fn server() { future::join(server, client).await; } +#[tokio::test] +async fn server_rpk() { + let (stream, addr) = create_server(true); + + let server = async { + let mut stream = stream.await.unwrap(); + let mut buf = [0; 4]; + stream.read_exact(&mut buf).await.unwrap(); + assert_eq!(&buf, b"asdf"); + + stream.write_all(b"jkl;").await.unwrap(); + + future::poll_fn(|ctx| Pin::new(&mut stream).poll_shutdown(ctx)) + .await + .unwrap(); + }; + + let client = async { + let mut connector = SslConnector::rpk_builder().unwrap(); + let cert = std::fs::read("tests/pubkey.der").unwrap(); + + connector.set_rpk_certificate(&cert).unwrap(); + let config = connector.build().configure().unwrap(); + + let stream = TcpStream::connect(&addr).await.unwrap(); + let mut stream = tokio_boring::connect(config, "localhost", stream) + .await + .unwrap(); + + stream.write_all(b"asdf").await.unwrap(); + + let mut buf = vec![]; + stream.read_to_end(&mut buf).await.unwrap(); + assert_eq!(buf, b"jkl;"); + }; + + future::join(server, client).await; +} + +#[tokio::test] +async fn client_rpk_unknown_cert() { + let (stream, addr) = create_server(true); + + let server = async { + assert!(stream.await.is_err()); + }; + + let client = async { + let mut connector = SslConnector::rpk_builder().unwrap(); + let cert = std::fs::read("tests/pubkey2.der").unwrap(); + + connector.set_rpk_certificate(&cert).unwrap(); + let config = connector.build().configure().unwrap(); + + let stream = TcpStream::connect(&addr).await.unwrap(); + + assert!(tokio_boring::connect(config, "localhost", stream) + .await + .is_err()); + }; + + future::join(server, client).await; +} + #[tokio::test] async fn handshake_error() { - let (stream, addr) = create_server(); + let (stream, addr) = create_server(false); let server = async { let err = stream.await.unwrap_err(); diff --git a/tokio-boring/tests/pubkey.der b/tokio-boring/tests/pubkey.der new file mode 100644 index 0000000000000000000000000000000000000000..7e5944f129349da4b95e5df9fa9db7a065922226 GIT binary patch literal 294 zcmV+>0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>lz9a)lpoLv~F!;3J zdrF5=3gVMG&YZ^0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>l&z8&lFt4ceKdZ%pwUh0jbB0Nxs7JZJ|NCU& z25;aS`OD0D%-;pMJ>Gn?Dbrd~=zZrN|EEK+-(F{@W6`>V^g#*0_c1^9=65BicW~Bc zHsm@4G3AlyGfiGeg@zlI8b79Mb`#Mwg|CfT#7lp6Nl}PoVI9<{$!!p;i-J&&RbX2_ sVx8cIR5Xe$q@xu