From c5982ec6b6bbd4e0f71ad4cbb9db579c6b95d474 Mon Sep 17 00:00:00 2001 From: James Mayclin Date: Mon, 1 Jul 2024 01:10:59 +0000 Subject: [PATCH 1/3] refactor(bindings/s2n-tls): finish test harness refactor --- bindings/rust/s2n-tls/Cargo.toml | 1 - bindings/rust/s2n-tls/src/callbacks/pkey.rs | 47 +--- bindings/rust/s2n-tls/src/testing.rs | 180 +------------ .../rust/s2n-tls/src/testing/resumption.rs | 79 ++---- bindings/rust/s2n-tls/src/testing/s2n_tls.rs | 246 +----------------- 5 files changed, 50 insertions(+), 503 deletions(-) diff --git a/bindings/rust/s2n-tls/Cargo.toml b/bindings/rust/s2n-tls/Cargo.toml index 2fac580b995..885ae2a19c5 100644 --- a/bindings/rust/s2n-tls/Cargo.toml +++ b/bindings/rust/s2n-tls/Cargo.toml @@ -24,7 +24,6 @@ pin-project-lite = "0.2" hex = "0.4" [dev-dependencies] -bytes = "1" futures-test = "0.3" openssl = "0.10" temp-env = "0.3" diff --git a/bindings/rust/s2n-tls/src/callbacks/pkey.rs b/bindings/rust/s2n-tls/src/callbacks/pkey.rs index 09e60d55874..e4977b0cf94 100644 --- a/bindings/rust/s2n-tls/src/callbacks/pkey.rs +++ b/bindings/rust/s2n-tls/src/callbacks/pkey.rs @@ -121,8 +121,8 @@ pub trait PrivateKeyCallback: 'static + Send + Sync { mod tests { use super::*; use crate::{ - config, connection, error, security, testing, - testing::{s2n_tls::*, *}, + config, connection, error, security, + testing::{self, *}, }; use core::task::{Poll, Waker}; use futures_test::task::new_count_waker; @@ -139,10 +139,7 @@ mod tests { "/../../../tests/pems/ecdsa_p384_pkcs1_cert.pem" )); - fn new_pair( - callback: T, - waker: Waker, - ) -> Result, Error> + fn new_pair(callback: T, waker: Waker) -> Result where T: 'static + PrivateKeyCallback, { @@ -157,20 +154,10 @@ mod tests { config.build()? }; - let server = { - let mut server = connection::Connection::new_server(); - server.set_config(config.clone())?; - server.set_waker(Some(&waker))?; - Harness::new(server) - }; - - let client = { - let mut client = connection::Connection::new_client(); - client.set_config(config)?; - Harness::new(client) - }; + let mut pair = TestPair::from_config(&config); + pair.server.set_waker(Some(&waker))?; - Ok(Pair::new(server, client)) + Ok(pair) } fn ecdsa_sign( @@ -214,11 +201,11 @@ mod tests { let (waker, wake_count) = new_count_waker(); let counter = testing::Counter::default(); let callback = TestPkeyCallback(counter.clone()); - let pair = new_pair(callback, waker)?; + let mut pair = new_pair(callback, waker)?; assert_eq!(counter.count(), 0); assert_eq!(wake_count, 0); - poll_tls_pair(pair); + pair.handshake()?; assert_eq!(counter.count(), 1); assert_eq!(wake_count, 0); @@ -272,11 +259,11 @@ mod tests { let (waker, wake_count) = new_count_waker(); let counter = testing::Counter::default(); let callback = TestPkeyCallback(counter.clone()); - let pair = new_pair(callback, waker)?; + let mut pair = new_pair(callback, waker)?; assert_eq!(counter.count(), 0); assert_eq!(wake_count, 0); - poll_tls_pair(pair); + pair.handshake()?; assert_eq!(counter.count(), 1); assert_eq!(wake_count, POLL_COUNT); @@ -306,14 +293,11 @@ mod tests { assert_eq!(counter.count(), 0); assert_eq!(wake_count, 0); - let result = poll_tls_pair_result(&mut pair); + let err = pair.handshake().unwrap_err(); assert_eq!(counter.count(), 1); assert_eq!(wake_count, 0); - match result { - Ok(_) => panic!("Handshake unexpectedly succeeded"), - Err(e) => testing::assert_test_error(e, ERROR), - }; + assert_test_error(err, ERROR); Ok(()) } @@ -362,14 +346,11 @@ mod tests { assert_eq!(counter.count(), 0); assert_eq!(wake_count, 0); - let result = poll_tls_pair_result(&mut pair); + let err = pair.handshake().unwrap_err(); assert_eq!(counter.count(), 1); assert_eq!(wake_count, POLL_COUNT); - match result { - Ok(_) => panic!("Handshake unexpectedly succeeded"), - Err(e) => testing::assert_test_error(e, ERROR), - }; + assert_test_error(err, ERROR); Ok(()) } } diff --git a/bindings/rust/s2n-tls/src/testing.rs b/bindings/rust/s2n-tls/src/testing.rs index feb5afc9b6e..2626cb07c23 100644 --- a/bindings/rust/s2n-tls/src/testing.rs +++ b/bindings/rust/s2n-tls/src/testing.rs @@ -7,10 +7,9 @@ use crate::{ connection, enums::{self, Blinding}, error, security, - testing::s2n_tls::Harness, }; use alloc::{collections::VecDeque, sync::Arc}; -use bytes::Bytes; + use core::{ sync::atomic::{AtomicUsize, Ordering}, task::Poll, @@ -33,15 +32,12 @@ pub fn test_error(msg: &str) -> crate::error::Error { crate::error::Error::application(msg.into()) } -pub fn assert_test_error(input: Error, msg: &str) { - let error = input - .downcast::() - .expect("Unexpected generic error type"); - if let Some(inner) = error.application_error() { - assert_eq!(msg, inner.to_string()) - } else { - panic!("Unexpected known error type"); - } +pub fn assert_test_error(input: crate::error::Error, expected_message: &str) { + let error_msg = input + .application_error() + .expect("unexpected error type") + .to_string(); + assert_eq!(expected_message, error_msg.to_string()) } #[derive(Clone)] @@ -63,123 +59,6 @@ impl Default for Counter { } } -pub trait Connection: core::fmt::Debug { - fn poll_negotiate(&mut self, context: &mut Ctx) -> Poll>; - fn poll_action(&mut self, context: &mut Ctx, action: F) -> Poll> - where - F: FnOnce(&mut connection::Connection) -> Poll>; -} - -pub trait Context { - fn receive(&mut self, max_len: Option) -> Option; - fn send(&mut self, data: Bytes); -} - -pub enum Mode { - Client, -} - -#[derive(Debug)] -pub struct Pair { - pub server: (Server, MemoryContext), - pub client: (Client, MemoryContext), - pub max_iterations: usize, -} - -impl Pair { - /// The number of iterations that will be executed until the handshake exits with an error - /// - /// This is to prevent endless looping without making progress on the connection. - const DEFAULT_ITERATIONS: usize = 100; - - pub fn new(server: Server, client: Client) -> Self { - Self { - server: (server, Default::default()), - client: (client, Default::default()), - max_iterations: Self::DEFAULT_ITERATIONS, - } - } - pub fn poll(&mut self) -> Poll> { - assert!( - self.max_iterations > 0, - "handshake has iterated too many times: {:#?}", - self, - ); - let client_res = self.client.0.poll_negotiate(&mut self.client.1); - let server_res = self.server.0.poll_negotiate(&mut self.server.1); - self.client.1.transfer(&mut self.server.1); - self.max_iterations -= 1; - match (client_res, server_res) { - (Poll::Ready(client_res), Poll::Ready(server_res)) => { - client_res?; - server_res?; - Ok(()).into() - } - (Poll::Ready(client_res), _) => { - client_res?; - Poll::Pending - } - (_, Poll::Ready(server_res)) => { - server_res?; - Poll::Pending - } - _ => Poll::Pending, - } - } - - pub fn poll_recv(&mut self, receiver: Mode, buf: &mut [u8]) -> Poll> { - let result = match receiver { - Mode::Client => self.client.0.poll_action(&mut self.client.1, |conn| { - connection::Connection::poll_recv(conn, buf) - }), - }; - match result { - Poll::Ready(result) => { - result?; - Ok(()).into() - } - Poll::Pending => Poll::Pending, - } - } -} - -#[derive(Debug, Default)] -pub struct MemoryContext { - rx: VecDeque, - tx: VecDeque, -} - -impl MemoryContext { - pub fn transfer(&mut self, other: &mut Self) { - self.rx.extend(other.tx.drain(..)); - other.rx.extend(self.tx.drain(..)); - } -} - -impl Context for MemoryContext { - fn receive(&mut self, max_len: Option) -> Option { - loop { - let mut chunk = self.rx.pop_front()?; - - if chunk.is_empty() { - continue; - } - - let max_len = max_len.unwrap_or(usize::MAX); - - if chunk.len() > max_len { - self.rx.push_front(chunk.split_off(max_len)); - } - - return Some(chunk); - } - } - - fn send(&mut self, data: Bytes) { - self.tx.push_back(data); - } -} - pub struct CertKeyPair { cert_path: &'static str, cert: &'static [u8], @@ -249,51 +128,6 @@ pub fn config_builder(cipher_prefs: &security::Policy) -> Result Pair { - // create and configure a server connection - let mut server = crate::connection::Connection::new_server(); - // some tests check for connection failure so disable blinding to avoid delay - server.as_mut().set_blinding(Blinding::SelfService).unwrap(); - server - .set_config(config.clone()) - .expect("Failed to bind config to server connection"); - let server = Harness::new(server); - - // create a client connection - let mut client = crate::connection::Connection::new_client(); - // some tests check for connection failure so disable blinding to avoid delay - client.as_mut().set_blinding(Blinding::SelfService).unwrap(); - client - .set_config(config) - .expect("Unable to set client config"); - let client = Harness::new(client); - - Pair::new(server, client) -} - -pub fn poll_tls_pair(mut pair: Pair) -> Pair { - loop { - match pair.poll() { - Poll::Ready(result) => { - result.unwrap(); - break; - } - Poll::Pending => continue, - } - } - - pair -} - -pub fn poll_tls_pair_result(pair: &mut Pair) -> Result<()> { - loop { - match pair.poll() { - Poll::Ready(result) => return result, - Poll::Pending => continue, - } - } -} - type LocalDataBuffer = RefCell>; /// TestPair is a testing utility used to easily test handshakes and send data. diff --git a/bindings/rust/s2n-tls/src/testing/resumption.rs b/bindings/rust/s2n-tls/src/testing/resumption.rs index cf4d28ccf22..acb11d631b3 100644 --- a/bindings/rust/s2n-tls/src/testing/resumption.rs +++ b/bindings/rust/s2n-tls/src/testing/resumption.rs @@ -7,7 +7,7 @@ mod tests { callbacks::{SessionTicket, SessionTicketCallback}, config::ConnectionInitializer, connection::{self, Connection}, - testing::{s2n_tls::*, *}, + testing::*, }; use futures_test::task::noop_waker; use std::{error::Error, sync::Mutex, time::SystemTime}; @@ -140,63 +140,26 @@ mod tests { .set_security_policy(&security::DEFAULT_TLS13)?; let client_config = client_config_builder.build()?; - // create and configure a server connection - let mut server = connection::Connection::new_server(); - server - .set_config(server_config.clone()) - .expect("Failed to bind config to server connection"); - - // create a client connection - let mut client = connection::Connection::new_client(); - client - .set_waker(Some(&noop_waker()))? - .set_config(client_config.clone()) - .expect("Unable to set client config"); - - let server = Harness::new(server); - let client = Harness::new(client); - let pair = Pair::new(server, client); - let mut pair = poll_tls_pair(pair); - - // Do a recv call on the client side to read a session ticket. Poll function - // returns pending since no application data was read, however it is enough - // to collect the session ticket. - assert!(pair.poll_recv(Mode::Client, &mut [0]).is_pending()); - - let client = pair.client.0.connection(); - // Check connection was full handshake - assert!(!client.resumed()); - // validate that a ticket is available - validate_session_ticket(client)?; - - // create and configure a client/server connection again - let mut server = connection::Connection::new_server(); - server - .set_config(server_config) - .expect("Failed to bind config to server connection"); - - // create a client connection with a resumption ticket - let mut client = connection::Connection::new_client(); - client - .set_waker(Some(&noop_waker()))? - .set_config(client_config) - .expect("Unable to set client config"); - - let server = Harness::new(server); - let client = Harness::new(client); - let pair = Pair::new(server, client); - let mut pair = poll_tls_pair(pair); - - // Do a recv call on the client side to read a session ticket. Poll function - // returns pending since no application data was read, however it is enough - // to collect the session ticket. - assert!(pair.poll_recv(Mode::Client, &mut [0]).is_pending()); - - let client = pair.client.0.connection(); - // Check new connection was resumed - assert!(client.resumed()); - // validate that a ticket is available - validate_session_ticket(client)?; + // 1st handshake: no session ticket, so no resumption + // 2nd handshake: should be able to use the session ticket from the first + // handshake (stored on the config) to resume + for expected_resumption in [false, true] { + let mut pair = TestPair::from_configs(&client_config, &server_config); + // Client needs a waker due to its use of an async callback + pair.client.set_waker(Some(&noop_waker()))?; + pair.handshake()?; + + // Do a recv call on the client side to read a session ticket. Poll function + // returns pending since no application data was read, however it is enough + // to collect the session ticket. + assert!(pair.client.poll_recv(&mut [0]).is_pending()); + + // assert the resumption status + assert_eq!(pair.client.resumed(), expected_resumption); + + // validate that a ticket is available + validate_session_ticket(&pair.client)?; + } Ok(()) } } diff --git a/bindings/rust/s2n-tls/src/testing/s2n_tls.rs b/bindings/rust/s2n-tls/src/testing/s2n_tls.rs index ed4ad974bdd..8d670a99e2c 100644 --- a/bindings/rust/s2n-tls/src/testing/s2n_tls.rs +++ b/bindings/rust/s2n-tls/src/testing/s2n_tls.rs @@ -1,238 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use crate::{ - connection::Connection, - testing::{Context, Error, Result}, -}; -use bytes::BytesMut; -use core::task::Poll; -use libc::c_void; -use s2n_tls_sys::s2n_status_code::Type as s2n_status_code; - -const SEND_BUFFER_CAPACITY: usize = 4096; - -#[derive(Debug)] -pub struct Harness { - connection: Connection, - send_buffer: BytesMut, - handshake_done: bool, - // TODO add a size -} - -impl Harness { - pub fn new(connection: Connection) -> Self { - Self { - connection, - send_buffer: BytesMut::new(), - handshake_done: false, - } - } - - pub fn connection(&self) -> &Connection { - &self.connection - } -} - -impl super::Connection for Harness { - fn poll_negotiate(&mut self, context: &mut Ctx) -> Poll> { - let mut callback: Callback = Callback { - context, - err: None, - send_buffer: &mut self.send_buffer, - }; - - unsafe { - // Safety: the callback struct must live as long as the callbacks are - // set on on the connection - callback.set(&mut self.connection); - } - - let result = self.connection.poll_negotiate().map_ok(|_| ()); - - callback.unset(&mut self.connection)?; - - match result { - Poll::Ready(Ok(_)) => { - if !self.handshake_done { - self.handshake_done = true; - } - Ok(()).into() - } - Poll::Ready(Err(err)) => Err(err.into()).into(), - Poll::Pending => Poll::Pending, - } - } - - fn poll_action(&mut self, context: &mut Ctx, action: F) -> Poll> - where - F: FnOnce(&mut Connection) -> Poll>, - { - let mut callback: Callback = Callback { - context, - err: None, - send_buffer: &mut self.send_buffer, - }; - - unsafe { - // Safety: the callback struct must live as long as the callbacks are - // set on on the connection - callback.set(&mut self.connection); - } - - let result = action(&mut self.connection); - - callback.unset(&mut self.connection)?; - - match result { - Poll::Ready(Ok(_)) => Ok(()).into(), - Poll::Ready(Err(err)) => Err(err.into()).into(), - Poll::Pending => Poll::Pending, - } - } -} - -struct Callback<'a, T> { - pub context: &'a mut T, - pub err: Option, - pub send_buffer: &'a mut BytesMut, -} - -impl<'a, T: 'a + Context> Callback<'a, T> { - unsafe fn set(&mut self, connection: &mut Connection) { - let context = self as *mut Self as *mut c_void; - - // We use unwrap here since s2n-tls will just check if connection is not null - connection.set_send_callback(Some(Self::send_cb)).unwrap(); - connection.set_send_context(context).unwrap(); - connection - .set_receive_callback(Some(Self::recv_cb)) - .unwrap(); - connection.set_receive_context(context).unwrap(); - } - - /// Removes all of the callback and context pointers from the connection - pub fn unset(mut self, connection: &mut Connection) -> Result<()> { - unsafe { - unsafe extern "C" fn send_cb( - _context: *mut c_void, - _data: *const u8, - _len: u32, - ) -> s2n_status_code { - -1 - } - - unsafe extern "C" fn recv_cb( - _context: *mut c_void, - _data: *mut u8, - _len: u32, - ) -> s2n_status_code { - -1 - } - - // We use unwrap here since s2n-tls will just check if connection is not null - connection.set_send_callback(Some(send_cb)).unwrap(); - connection.set_send_context(core::ptr::null_mut()).unwrap(); - connection.set_receive_callback(Some(recv_cb)).unwrap(); - connection - .set_receive_context(core::ptr::null_mut()) - .unwrap(); - - // Flush the send buffer before returning to the connection - self.flush(); - - if let Some(err) = self.err { - return Err(err); - } - - Ok(()) - } - } - - unsafe extern "C" fn send_cb( - context: *mut c_void, - data: *const u8, - len: u32, - ) -> s2n_status_code { - let context = &mut *(context as *mut Self); - let data = core::slice::from_raw_parts(data, len as _); - context.on_write(data) as _ - } - - /// Called when sending data - fn on_write(&mut self, data: &[u8]) -> usize { - // If this write would cause the current send buffer to reallocate, - // we should flush and create a new send buffer. - let remaining_capacity = self.send_buffer.capacity() - self.send_buffer.len(); - - if remaining_capacity < data.len() { - // Flush the send buffer before reallocating it - self.flush(); - - // ensure we only do one allocation for this write - let len = SEND_BUFFER_CAPACITY.max(data.len()); - - debug_assert!( - self.send_buffer.is_empty(), - "dropping a send buffer with data will result in data loss" - ); - *self.send_buffer = BytesMut::with_capacity(len); - } - - // Write the current data to the send buffer - // - // NOTE: we don't immediately flush to the context since s2n-tls may do - // several small writes in a row. - self.send_buffer.extend_from_slice(data); - - data.len() - } - - /// Flushes the send buffer into the context - fn flush(&mut self) { - if !self.send_buffer.is_empty() { - let chunk = self.send_buffer.split().freeze(); - self.context.send(chunk); - } - } - - /// The function s2n-tls calls when it wants to receive data - unsafe extern "C" fn recv_cb(context: *mut c_void, data: *mut u8, len: u32) -> s2n_status_code { - let context = &mut *(context as *mut Self); - let data = core::slice::from_raw_parts_mut(data, len as _); - match context.on_read(data) { - 0 => { - // https://aws.github.io/s2n-tls/doxygen/s2n_8h.html#a699fd9e05a8e8163811db6cab01af973 - // s2n-tls wants us to set the global errno to signal blocked - errno::set_errno(errno::Errno(libc::EWOULDBLOCK)); - -1 - } - len => len as _, - } - } - - /// Called when receiving data - fn on_read(&mut self, data: &mut [u8]) -> usize { - let max_len = Some(data.len()); - - // TODO: loop until data buffer is full. - if let Some(chunk) = self.context.receive(max_len) { - let len = chunk.len(); - data[..len].copy_from_slice(&chunk); - len - } else { - 0 - } - } -} - #[cfg(test)] mod tests { use crate::{ callbacks::{ClientHelloCallback, ConnectionFuture, ConnectionFutureResult}, enums::ClientAuthType, error::ErrorType, - testing::{self, client_hello::*, s2n_tls::*, *}, + testing::{self, client_hello::*, Error, Result, *}, }; use alloc::sync::Arc; use core::sync::atomic::Ordering; @@ -867,26 +642,21 @@ mod tests { #[test] fn no_application_protocol() -> Result<(), Error> { let config = config_builder(&security::DEFAULT)?.build()?; - let mut pair = tls_pair(config); - assert!(poll_tls_pair_result(&mut pair).is_ok()); - assert!(pair.server.0.connection.application_protocol().is_none()); + let mut pair = TestPair::from_config(&config); + pair.handshake()?; + assert!(pair.server.application_protocol().is_none()); Ok(()) } #[test] fn application_protocol() -> Result<(), Error> { let config = config_builder(&security::DEFAULT)?.build()?; - let mut pair = tls_pair(config); + let mut pair = TestPair::from_config(&config); pair.server - .0 - .connection .set_application_protocol_preference(["http/1.1", "h2"])?; - pair.client - .0 - .connection - .append_application_protocol_preference(b"h2")?; - assert!(poll_tls_pair_result(&mut pair).is_ok()); - let protocol = pair.server.0.connection.application_protocol().unwrap(); + pair.client.append_application_protocol_preference(b"h2")?; + pair.handshake()?; + let protocol = pair.server.application_protocol().unwrap(); assert_eq!(protocol, b"h2"); Ok(()) } From eb85af33b89c8c2065822ddf8760ee971b70ed3a Mon Sep 17 00:00:00 2001 From: James Mayclin Date: Mon, 1 Jul 2024 20:26:09 +0000 Subject: [PATCH 2/3] address ci failures - I missed the tests hiding behind the conditional flag --- bindings/rust/s2n-tls/src/client_hello.rs | 58 +++++++++-------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/bindings/rust/s2n-tls/src/client_hello.rs b/bindings/rust/s2n-tls/src/client_hello.rs index db27e5f5c49..2ef8b76de61 100644 --- a/bindings/rust/s2n-tls/src/client_hello.rs +++ b/bindings/rust/s2n-tls/src/client_hello.rs @@ -229,30 +229,21 @@ pub mod fingerprint { fingerprint::{FingerprintType, MD5_HASH_SIZE}, ClientHello, }, - connection::Connection, error::{Error, ErrorType}, security, - testing::{poll_tls_pair, tls_pair}, + testing::TestPair, }; /// This function is a test fixture used a generate a valid ClientHello so /// that we don't have to copy and paste the raw bytes for test fixtures - fn get_client_hello_bytes() -> Vec { + fn get_client_hello_bytes() -> Result, crate::error::Error> { let config = crate::testing::config_builder(&security::DEFAULT_TLS13) .unwrap() - .build() - .unwrap(); - let pair = tls_pair(config); - let pair = poll_tls_pair(pair); + .build()?; + let mut pair = TestPair::from_config(&config); + pair.handshake().unwrap(); // this doesn't have the handshake header - let client_hello_message = pair - .server - .0 - .connection() - .client_hello() - .unwrap() - .raw_message() - .unwrap(); + let client_hello_message = pair.server.client_hello()?.raw_message()?; // handshake header is {tag: u8, client_hello_length: u24} let mut client_hello = vec![0; 4]; // As long as the client hello is small, no bit fiddling is required @@ -261,7 +252,7 @@ pub mod fingerprint { client_hello[0] = 1; client_hello[3] = client_hello_message.len() as u8; client_hello.extend(client_hello_message.iter()); - client_hello + Ok(client_hello) } fn known_test_case( @@ -290,7 +281,7 @@ pub mod fingerprint { pub fn get_client_hello() -> Box { // sets up connection and handshakes let raw_client_hello = get_client_hello_bytes(); - ClientHello::parse_client_hello(raw_client_hello.as_slice()).unwrap() + ClientHello::parse_client_hello(raw_client_hello.unwrap().as_slice()).unwrap() } pub fn client_hello_bytes() -> Vec { @@ -324,28 +315,23 @@ pub mod fingerprint { .unwrap() .build() .unwrap(); - let pair = crate::testing::tls_pair(config); + let mut pair = TestPair::from_config(&config); // client_hellos can not be accessed before the handshake - assert!(pair.client.0.connection().client_hello().is_err()); - assert!(pair.server.0.connection().client_hello().is_err()); - - let pair = poll_tls_pair(pair); - let server_conn = pair.server.0.connection(); - let client_conn = pair.server.0.connection(); - - let check_client_hello = |conn: &Connection| -> Result<(), Error> { - let client_hello = conn.client_hello().unwrap(); - let mut hash = Vec::new(); - let fingerprint_size = - client_hello.fingerprint_hash(FingerprintType::JA3, &mut hash)?; - let mut string = String::with_capacity(fingerprint_size as usize); - client_hello.fingerprint_string(FingerprintType::JA3, &mut string)?; - Ok(()) - }; + assert!(pair.client.client_hello().is_err()); + assert!(pair.server.client_hello().is_err()); + + pair.handshake().unwrap(); - assert!(check_client_hello(server_conn).is_ok()); - assert!(check_client_hello(client_conn).is_ok()); + let client_hello = pair.server.client_hello().unwrap(); + let mut hash = Vec::new(); + let fingerprint_size = client_hello + .fingerprint_hash(FingerprintType::JA3, &mut hash) + .unwrap(); + let mut string = String::with_capacity(fingerprint_size as usize); + client_hello + .fingerprint_string(FingerprintType::JA3, &mut string) + .unwrap(); } // known value test case copied from s2n_fingerprint_ja3_test.c From d0fd71505c90e558f37ffa97ecefadf79c3ad6b5 Mon Sep 17 00:00:00 2001 From: James Mayclin Date: Tue, 9 Jul 2024 10:59:27 -0700 Subject: [PATCH 3/3] Update bindings/rust/s2n-tls/src/client_hello.rs Co-authored-by: Sam Clark <3758302+goatgoose@users.noreply.github.com> --- bindings/rust/s2n-tls/src/client_hello.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/rust/s2n-tls/src/client_hello.rs b/bindings/rust/s2n-tls/src/client_hello.rs index 2ef8b76de61..a68eab47ab9 100644 --- a/bindings/rust/s2n-tls/src/client_hello.rs +++ b/bindings/rust/s2n-tls/src/client_hello.rs @@ -241,7 +241,7 @@ pub mod fingerprint { .unwrap() .build()?; let mut pair = TestPair::from_config(&config); - pair.handshake().unwrap(); + pair.handshake()?; // this doesn't have the handshake header let client_hello_message = pair.server.client_hello()?.raw_message()?; // handshake header is {tag: u8, client_hello_length: u24}