Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expect loop forever #39

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 10 additions & 18 deletions bindings/rust/s2n-tls-tokio/tests/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,26 +163,18 @@ async fn handshake_error_with_blinding() -> Result<(), Box<dyn std::error::Error

let client = TlsConnector::new(client_config.clone());
let server = TlsAcceptor::new(server_config.clone());
let (server_stream, client_stream) = common::get_streams().await?;

let time_start = time::Instant::now();
let result = common::run_negotiate(&client, client_stream, &server, server_stream).await;
let time_elapsed = time_start.elapsed();

// Handshake MUST NOT finish faster than minimal blinding time.
let (server_stream, client_stream) = common::get_streams().await?;
let timeout = time::timeout(
common::MIN_BLINDING_SECS,
common::run_negotiate(&client, client_stream, &server, server_stream),
)
.await;
assert!(timeout.is_err());

// Handshake MUST eventually gracefully close after blinding
let (server_stream, client_stream) = common::get_streams().await?;
let timeout = time::timeout(
common::MAX_BLINDING_SECS.mul_f32(1.1),
common::run_negotiate(&client, client_stream, &server, server_stream),
)
.await;
let result = timeout?;
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), ErrorType::ProtocolError);
assert!(time_elapsed > common::MIN_BLINDING_SECS);

// Handshake MUST eventually gracefully fail after blinding
let error = result.unwrap_err();
assert_eq!(error.kind(), ErrorType::ProtocolError);

Ok(())
}
Expand Down
114 changes: 61 additions & 53 deletions bindings/rust/s2n-tls-tokio/tests/shutdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ use s2n_tls_tokio::{TlsAcceptor, TlsConnector, TlsStream};
use std::{convert::TryFrom, sync::Arc};
use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
join, time,
time,
};

pub mod common;

// An arbitrary but very long timeout.
// No valid single IO operation should take anywhere near 10 minutes.
pub const LONG_TIMEOUT: time::Duration = time::Duration::from_secs(600);

async fn read_until_shutdown<S: AsyncRead + AsyncWrite + Unpin>(
stream: &mut TlsStream<S>,
) -> Result<(), std::io::Error> {
Expand Down Expand Up @@ -144,6 +148,7 @@ async fn shutdown_after_halfclose_split() -> Result<(), Box<dyn std::error::Erro

#[tokio::test(start_paused = true)]
async fn shutdown_with_blinding() -> Result<(), Box<dyn std::error::Error>> {
loop {
let clock = common::TokioTime::default();
let mut server_config = common::server_config()?;
server_config.set_monotonic_clock(clock)?;
Expand All @@ -157,36 +162,46 @@ async fn shutdown_with_blinding() -> Result<(), Box<dyn std::error::Error>> {
let (mut client, mut server) =
common::run_negotiate(&client, client_stream, &server, server_stream).await?;

// Trigger a blinded error for the server.
// Attempt to shutdown the client. This will eventually fail because the
// server has not written the close_notify message yet, but it will at least
// write the close_notify message that the server needs.
// Because time is mocked for testing, this does not actually take LONG_TIMEOUT.
// TODO: replace this with a half-close once the bindings support half-close.
let timeout = time::timeout(LONG_TIMEOUT, client.shutdown()).await;
assert!(timeout.is_err());

// Setup a bad record for the next read
overrides.next_read(Some(Box::new(|_, _, buf| {
// Parsing the header is one of the blinded operations
// in s2n_recv, so provide a malformed header.
let zeroed_header = [23, 0, 0, 0, 0];
buf.put_slice(&zeroed_header);
Ok(()).into()
})));

// Trigger the blinded error
let mut received = [0; 1];
let result = server.read_exact(&mut received).await;
assert!(result.is_err());

// Shutdown MUST NOT complete faster than minimal blinding time.
let (timeout, _) = join!(
time::timeout(common::MIN_BLINDING_SECS, server.shutdown()),
time::timeout(common::MIN_BLINDING_SECS, read_until_shutdown(&mut client)),
);
assert!(timeout.is_err());

// Shutdown MUST eventually complete after blinding.
//
// We check for completion, but not for success. At the moment, the
// call to s2n_shutdown will fail due to issues in the underlying C library.
let (timeout, _) = join!(
time::timeout(common::MAX_BLINDING_SECS, server.shutdown()),
time::timeout(common::MAX_BLINDING_SECS, read_until_shutdown(&mut client)),
);
assert!(timeout.is_ok());
let time_start = time::Instant::now();
let result = server.shutdown().await;
let time_elapsed = time_start.elapsed();

Ok(())
// Shutdown MUST NOT complete faster than minimal blinding time.
assert!(time_elapsed > common::MIN_BLINDING_SECS);

// TODO: While the server SHOULD successfully shutdown, there is currently
// a C bug preventing it from doing so: https://github.com/aws/s2n-tls/pull/4350
let io_error = result.unwrap_err();
let error: error::Error = io_error.try_into()?;
assert!(error.kind() == error::ErrorType::IOError);
assert!(error.name() == "S2N_ERR_IO");

// Shutdown MUST have sent the close_notify message needed by the peer
// to also shutdown successfully.
client.shutdown().await?;
}
}

#[tokio::test(start_paused = true)]
Expand All @@ -204,33 +219,31 @@ async fn shutdown_with_blinding_bad_close_record() -> Result<(), Box<dyn std::er
let (mut client, mut server) =
common::run_negotiate(&client, client_stream, &server, server_stream).await?;

// Turn the closure alert to a bad message
// Setup a bad record for the next read
overrides.next_read(Some(Box::new(|_, _, buf| {
// Parsing the header is one of the blinded operations
// in s2n_recv, so provide a malformed header.
// in s2n_shutdown, so provide a malformed header.
let zeroed_header = [23, 0, 0, 0, 0];
buf.put_slice(&zeroed_header);
Ok(()).into()
})));

let time_start = time::Instant::now();
let result = server.shutdown().await;
let time_elapsed = time_start.elapsed();

// Shutdown MUST NOT complete faster than minimal blinding time.
let (timeout, _) = join!(
time::timeout(common::MIN_BLINDING_SECS, server.shutdown()),
time::timeout(common::MIN_BLINDING_SECS, read_until_shutdown(&mut client)),
);
assert!(timeout.is_err());
assert!(time_elapsed > common::MIN_BLINDING_SECS);

// Shutdown MUST eventually complete after blinding.
//
// We check for completion, but not for success. At the moment, the
// call to s2n_shutdown will fail due to issues in the underlying C library.
let (timeout, _) = join!(
time::timeout(common::MAX_BLINDING_SECS, server.shutdown()),
time::timeout(common::MAX_BLINDING_SECS, read_until_shutdown(&mut client)),
);
// timeout should be OK, but shutdown should return an error because of
// the bad record.
assert!(matches!(timeout, Ok(Err(_))));
// Shutdown MUST eventually complete with the correct error after blinding.
let io_error = result.unwrap_err();
let error: error::Error = io_error.try_into()?;
assert!(error.kind() == error::ErrorType::ProtocolError);
assert!(error.name() == "S2N_ERR_BAD_MESSAGE");

// Shutdown MUST have sent the close_notify message needed by the peer
// to also shutdown successfully.
client.shutdown().await?;

Ok(())
}
Expand All @@ -247,37 +260,32 @@ async fn shutdown_with_poll_blinding() -> Result<(), Box<dyn std::error::Error>>
let (server_stream, client_stream) = common::get_streams().await?;
let server_stream = common::TestStream::new(server_stream);
let overrides = server_stream.overrides();
let (mut client, mut server) =
let (_, mut server) =
common::run_negotiate(&client, client_stream, &server, server_stream).await?;

// Trigger a blinded error for the server.
// Setup a bad record for the next read
overrides.next_read(Some(Box::new(|_, _, buf| {
// Parsing the header is one of the blinded operations
// in s2n_recv, so provide a malformed header.
let zeroed_header = [23, 0, 0, 0, 0];
buf.put_slice(&zeroed_header);
Ok(()).into()
})));

// Trigger the blinded error
let mut received = [0; 1];
let result = server.read_exact(&mut received).await;
assert!(result.is_err());

let time_start = time::Instant::now();
let result = server.apply_blinding().await;
let time_elapsed = time_start.elapsed();

// poll_blinding MUST NOT complete faster than minimal blinding time.
let (timeout, _) = join!(
time::timeout(common::MIN_BLINDING_SECS, server.apply_blinding()),
time::timeout(common::MIN_BLINDING_SECS, read_until_shutdown(&mut client)),
);
assert!(timeout.is_err());
assert!(time_elapsed > common::MIN_BLINDING_SECS);

// Shutdown MUST eventually complete after blinding.
//
// We check for completion, but not for success. At the moment, the
// call to s2n_shutdown will fail due to issues in the underlying C library.
let (timeout, _) = join!(
time::timeout(common::MAX_BLINDING_SECS, server.apply_blinding()),
time::timeout(common::MAX_BLINDING_SECS, read_until_shutdown(&mut client)),
);
assert!(timeout.is_ok());
// poll_blinding MUST eventually complete
assert!(result.is_ok());

Ok(())
}
Loading