diff --git a/.github/workflows/ci_rust.yml b/.github/workflows/ci_rust.yml index b8d8b7690a1..b515b146e0d 100644 --- a/.github/workflows/ci_rust.yml +++ b/.github/workflows/ci_rust.yml @@ -40,6 +40,11 @@ jobs: working-directory: ${{env.ROOT_PATH}} run: cargo test --all-features + # Ensure that all tests pass with the default feature set + - name: Default Tests + working-directory: ${{env.ROOT_PATH}} + run: cargo test + - name: Test external build # if this test is failing, make sure that api headers are appropriately # included. For a symbol to be visible in a shared lib, the diff --git a/api/s2n.h b/api/s2n.h index 0f78ff458e5..6ef207e090d 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -1812,6 +1812,86 @@ S2N_API extern int s2n_connection_prefer_throughput(struct s2n_connection *conn) */ S2N_API extern int s2n_connection_prefer_low_latency(struct s2n_connection *conn); +/** + * Configure the connection to reduce potentially expensive calls to recv. + * + * If this setting is disabled, s2n-tls will call read twice for every TLS record, + * which can be expensive but ensures that s2n-tls will always attempt to read the + * exact number of bytes it requires. If this setting is enabled, s2n-tls will + * instead reduce the number of calls to read by attempting to read as much data + * as possible in each read call, storing the extra in the existing IO buffers. + * This may cause it to request more data than will ever actually be available. + * + * There is no additional memory cost of enabling this setting. It reuses the + * existing IO buffers. + * + * This setting is disabled by default. Depending on how your application detects + * data available for reading, buffering reads may break your event loop. + * In particular, note that: + * + * 1. File descriptor reads or calls to your custom s2n_recv_cb may request more + * data than is available. Reads must return partial data when available rather + * than blocking until all requested data is available. + * + * 2. s2n_negotiate may read and buffer application data records. + * You must call s2n_recv at least once after negotiation to ensure that you + * handle any buffered data. + * + * 3. s2n_recv may read and buffer more records than it parses and decrypts. + * You must call s2n_recv until it reports S2N_ERR_T_BLOCKED, rather than just + * until it reports S2N_SUCCESS. + * + * 4. s2n_peek reports available decrypted data. It does not report any data + * buffered by this feature. However, s2n_peek_buffered does report data + * buffered by this feature. + * + * 5. s2n_connection_release_buffers will not release the input buffer if it + * contains buffered data. + * + * For example: if your event loop uses `poll`, you will receive a POLLIN event + * for your read file descriptor when new data is available. When you call s2n_recv + * to read that data, s2n-tls reads one or more TLS records from the file descriptor. + * If you stop calling s2n_recv before it reports S2N_ERR_T_BLOCKED, some of those + * records may remain in s2n-tls's read buffer. If you read part of a record, + * s2n_peek will report the remainder of that record as available. But if you don't + * read any of a record, it remains encrypted and is not reported by s2n_peek, but + * is still reported by s2n_peek_buffered. And because the data is buffered in s2n-tls + * instead of in the file descriptor, another call to `poll` will NOT report any + * more data available. Your application may hang waiting for more data. + * + * @warning This feature cannot be enabled for a connection that will enable kTLS for receiving. + * + * @warning This feature may work with blocking IO, if used carefully. Your blocking + * IO must support partial reads (so MSG_WAITALL cannot be used). You will either + * need to know exactly how much data your peer is sending, or will need to use + * `s2n_peek` and `s2n_peek_buffered` rather than relying on S2N_ERR_T_BLOCKED + * as noted in #3 above. + * + * @param conn The connection object being updated + * @param enabled Set to `true` to enable, `false` to disable. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure + */ +S2N_API extern int s2n_connection_set_recv_buffering(struct s2n_connection *conn, bool enabled); + +/** + * Reports how many bytes of unprocessed TLS records are buffered due to the optimization + * enabled by `s2n_connection_set_recv_buffering`. + * + * `s2n_peek_buffered` is not a replacement for `s2n_peek`. + * While `s2n_peek` reports application data that is ready for the application + * to read with no additional processing, `s2n_peek_buffered` reports raw TLS + * records that still need to be parsed and likely decrypted. Those records may + * contain application data, but they may also only contain TLS control messages. + * + * If an application needs to determine whether there is any data left to handle + * (for example, before calling `poll` to wait on the read file descriptor) then + * that application must check both `s2n_peek` and `s2n_peek_buffered`. + * + * @param conn A pointer to the s2n_connection object + * @returns The number of buffered encrypted bytes + */ +S2N_API extern uint32_t s2n_peek_buffered(struct s2n_connection *conn); + /** * Configure the connection to free IO buffers when they are not currently in use. * @@ -3599,6 +3679,90 @@ S2N_API int s2n_offered_early_data_accept(struct s2n_offered_early_data *early_d S2N_API int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max, uint16_t *groups_count); +/* Indicates which serialized connection version will be provided. The default value is + * S2N_SERIALIZED_CONN_NONE, which indicates the feature is off. + */ +typedef enum { + S2N_SERIALIZED_CONN_NONE = 0, + S2N_SERIALIZED_CONN_V1 = 1 +} s2n_serialization_version; + +/** + * Set what version to use when serializing connections + * + * A version is required to serialize connections. Versioning ensures that all features negotiated + * during the handshake will be available wherever the connection is deserialized. Applications may + * need to update this version to pick up new features, since versioning may disable newer TLS + * features to ensure compatibility. + * + * @param config A pointer to the config object. + * @param version The requested version. + * @returns S2N_SUCCESS on success, S2N_FAILURE on error. + */ +S2N_API int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serialization_version version); + +/** + * Retrieves the length of the serialized connection from `s2n_connection_serialize()`. Should be + * used to allocate enough memory for the serialized connection buffer. + * + * @note The size of the serialized connection changes based on parameters negotiated in the TLS + * handshake. Do not expect the size to always remain the same. + * + * @param conn A pointer to the connection object. + * @param length Output parameter where the length will be written. + * @returns S2N_SUCCESS on success, S2N_FAILURE on error. + */ +S2N_API int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length); + +/** + * Serializes the s2n_connection into the provided buffer. + * + * This API takes an established s2n-tls connection object and "serializes" it + * into a transferable object to be sent off-box or to another process. This transferable object can + * then be "deserialized" using the `s2n_connection_deserialize` method to instantiate an s2n-tls + * connection object that can talk to the original peer with the same encryption keys. + * + * @warning This feature is dangerous because it provides cryptographic material from a TLS session + * in plaintext. Users MUST both encrypt and MAC the contents of the outputted material to provide + * secrecy and integrity if this material is transported off-box. DO NOT store or send this material off-box + * without encryption. + * + * @note You MUST have used `s2n_config_set_serialization_version()` to set a version on the + * s2n_config object associated with this connection before this connection began its TLS handshake. + * @note Call `s2n_connection_serialization_length` to retrieve the amount of memory needed for the + * buffer parameter. + * @note This API will error if the handshake is not yet complete. + * + * @param conn A pointer to the connection object. + * @param buffer A pointer to the buffer where the serialized connection will be written. + * @param buffer_length Maximum amount of data that can be written to the buffer param. + * @returns S2N_SUCCESS on success, S2N_FAILURE on error. + */ +S2N_API int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); + +/** + * Deserializes the provided buffer into the `s2n_connection` parameter. + * + * @warning s2n-tls DOES NOT check the integrity of the provided buffer. s2n-tls may successfully + * deserialize a corrupted buffer which WILL cause a connection failure when attempting to resume + * sending/receiving encrypted data. To avoid this, it is recommended to MAC and encrypt the serialized + * connection before sending it off-box and deserializing it. + * + * @warning Only a minimal amount of information about the original TLS connection is serialized. + * Therefore, after deserialization, the connection will behave like a new `s2n_connection` from the + * `s2n_connection_new()` call, except that it can read/write encrypted data from a peer. Any desired + * config-level or connection-level configuration will need to be re-applied to the deserialized connection. + * For this same reason none of the connection getters will return useful information about the + * original connection after deserialization. Any information about the original connection needs to + * be retrieved before serialization. + * + * @param conn A pointer to the connection object. Should be a new s2n_connection object. + * @param buffer A pointer to the buffer where the serialized connection will be read from. + * @param buffer_length Maximum amount of data that can be read from the buffer parameter. + * @returns S2N_SUCCESS on success, S2N_FAILURE on error. + */ +S2N_API int s2n_connection_deserialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); + #ifdef __cplusplus } #endif diff --git a/api/unstable/ktls.h b/api/unstable/ktls.h index 0d9c28fd588..289698000bc 100644 --- a/api/unstable/ktls.h +++ b/api/unstable/ktls.h @@ -38,6 +38,7 @@ * The TLS kernel module currently doesn't support renegotiation. * - By default, you must negotiate TLS1.2. See s2n_config_ktls_enable_tls13 * for the requirements to also support TLS1.3. + * - You must not use s2n_connection_set_recv_buffering */ /** diff --git a/api/unstable/renegotiate.h b/api/unstable/renegotiate.h index b3e8e04d1ab..bf2c5412cda 100644 --- a/api/unstable/renegotiate.h +++ b/api/unstable/renegotiate.h @@ -110,6 +110,8 @@ S2N_API int s2n_config_set_renegotiate_request_cb(struct s2n_config *config, s2n * * @note This method MUST be called before s2n_renegotiate. * @note Calling this method on a server connection will fail. s2n-tls servers do not support renegotiation. + * @note This method will fail if the connection has indicated that it will be serialized with + * `s2n_config_set_serialization_version()`. * * @param conn A pointer to the connection object. * @returns S2N_SUCCESS on success, S2N_FAILURE on error. diff --git a/bindings/rust/s2n-tls-sys/build.rs b/bindings/rust/s2n-tls-sys/build.rs index f9dbdc2e34e..32b4efda1ea 100644 --- a/bindings/rust/s2n-tls-sys/build.rs +++ b/bindings/rust/s2n-tls-sys/build.rs @@ -19,7 +19,7 @@ fn env>(name: N) -> String { fn option_env>(name: N) -> Option { let name = name.as_ref(); - eprintln!("cargo:rerun-if-env-changed={}", name); + println!("cargo:rerun-if-env-changed={}", name); std::env::var(name).ok() } @@ -194,7 +194,7 @@ impl Default for Libcrypto { if let Some(version) = version.strip_suffix("_INCLUDE") { let version = version.to_string(); - eprintln!("cargo:rerun-if-env-changed={}", name); + println!("cargo:rerun-if-env-changed={}", name); let include = value; let root = env(format!("DEP_AWS_LC_{version}_ROOT")); diff --git a/bindings/rust/s2n-tls-sys/templates/Cargo.template b/bindings/rust/s2n-tls-sys/templates/Cargo.template index 8a7fe3b59b8..3b992645f48 100644 --- a/bindings/rust/s2n-tls-sys/templates/Cargo.template +++ b/bindings/rust/s2n-tls-sys/templates/Cargo.template @@ -1,7 +1,7 @@ [package] name = "s2n-tls-sys" description = "A C99 implementation of the TLS/SSL protocols" -version = "0.2.0" +version = "0.2.3" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" diff --git a/bindings/rust/s2n-tls-tokio/Cargo.toml b/bindings/rust/s2n-tls-tokio/Cargo.toml index b0437e624f9..cff8e706717 100644 --- a/bindings/rust/s2n-tls-tokio/Cargo.toml +++ b/bindings/rust/s2n-tls-tokio/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "s2n-tls-tokio" description = "An implementation of TLS streams for Tokio built on top of s2n-tls" -version = "0.2.0" +version = "0.2.3" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -15,7 +15,7 @@ default = [] errno = { version = "0.3" } libc = { version = "0.2" } pin-project-lite = { version = "0.2" } -s2n-tls = { version = "=0.2.0", path = "../s2n-tls" } +s2n-tls = { version = "=0.2.3", path = "../s2n-tls" } tokio = { version = "1", features = ["net", "time"] } [dev-dependencies] diff --git a/bindings/rust/s2n-tls/Cargo.toml b/bindings/rust/s2n-tls/Cargo.toml index bf59ff742f6..9e5ca5f7915 100644 --- a/bindings/rust/s2n-tls/Cargo.toml +++ b/bindings/rust/s2n-tls/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "s2n-tls" description = "A C99 implementation of the TLS/SSL protocols" -version = "0.2.0" +version = "0.2.3" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -11,6 +11,7 @@ license = "Apache-2.0" [features] default = [] unstable-fingerprint = ["s2n-tls-sys/unstable-fingerprint"] +unstable-ktls = ["s2n-tls-sys/unstable-ktls"] quic = ["s2n-tls-sys/quic"] pq = ["s2n-tls-sys/pq"] testing = ["bytes"] @@ -19,7 +20,7 @@ testing = ["bytes"] bytes = { version = "1", optional = true } errno = { version = "0.3" } libc = "0.2" -s2n-tls-sys = { version = "=0.2.0", path = "../s2n-tls-sys", features = ["internal"] } +s2n-tls-sys = { version = "=0.2.3", path = "../s2n-tls-sys", features = ["internal"] } pin-project-lite = "0.2" hex = "0.4" diff --git a/bindings/rust/s2n-tls/src/client_hello.rs b/bindings/rust/s2n-tls/src/client_hello.rs index dab6e2444db..db27e5f5c49 100644 --- a/bindings/rust/s2n-tls/src/client_hello.rs +++ b/bindings/rust/s2n-tls/src/client_hello.rs @@ -5,22 +5,6 @@ use crate::error::{Error, Fallible}; use s2n_tls_sys::*; use std::fmt; -#[derive(Copy, Clone)] -pub enum FingerprintType { - JA3, -} - -// this is the size of the MD5 hash digest that is used for the JA3 fingerprint -const MD5_HASH_SIZE: u32 = 16; - -impl From for s2n_tls_sys::s2n_fingerprint_type::Type { - fn from(value: FingerprintType) -> Self { - match value { - FingerprintType::JA3 => s2n_tls_sys::s2n_fingerprint_type::FINGERPRINT_JA3, - } - } -} - // ClientHello is an opaque wrapper struct around `s2n_client_hello`. Note that // the size of this type is not known, and as such it can only be used through // references and pointers. @@ -35,23 +19,6 @@ impl From for s2n_tls_sys::s2n_fingerprint_type::Type { // the connection struct. This is best represented as a reference tied to the // lifetime of the `Connection` struct. -/// ```no_run -/// use s2n_tls::client_hello::{ClientHello, FingerprintType}; -/// use s2n_tls::connection::Connection; -/// use s2n_tls::enums::Mode; -/// -/// let mut conn = Connection::new(Mode::Server); -/// // handshake happens -/// let mut client_hello: &ClientHello = conn.client_hello().unwrap(); -/// let mut hash = Vec::new(); -/// let string_size = client_hello.fingerprint_hash(FingerprintType::JA3, &mut hash).unwrap(); -/// // hash has been resized so that it can store the fingerprint hash -/// -/// let mut string = String::with_capacity(string_size as usize); -/// // string will not be resized, and the method will fail with -/// // ErrorType::UsageError if the string doesn't have enough capacity -/// client_hello.fingerprint_string(FingerprintType::JA3, &mut string).unwrap(); -/// ``` pub struct ClientHello(s2n_client_hello); impl ClientHello { @@ -89,65 +56,7 @@ impl ClientHello { &self.0 as *const s2n_client_hello as *mut s2n_client_hello } - /// `fingerprint_hash` calculates the hash, and also returns the size - /// required for the full fingerprint string. The return value can be used - /// to construct a string of appropriate capacity to call - /// `fingerprint_string`. `output` will be extended if necessary to store - /// the full hash. - pub fn fingerprint_hash( - &self, - hash: FingerprintType, - output: &mut Vec, - ) -> Result { - let mut hash_size: u32 = 0; - let mut str_size: u32 = 0; - // make sure the vec has sufficient space for the hash - if output.capacity() < MD5_HASH_SIZE as usize { - output.reserve_exact(MD5_HASH_SIZE as usize - output.len()); - } - unsafe { - s2n_client_hello_get_fingerprint_hash( - self.deref_mut_ptr(), - hash.into(), - MD5_HASH_SIZE, - output.as_mut_ptr(), - &mut hash_size, - &mut str_size, - ) - .into_result()?; - // SAFETY: we wrote to the raw vec (using the mut pointer), and need - // to update the state of the vec to reflect the changes we made. - output.set_len(hash_size as usize); - }; - Ok(str_size) - } - - /// `fingerprint_string` will try to calculate the fingerprint and store the - /// resulting string in `output`. If `output` does not have sufficient - /// capacity an Error of `ErrorType::UsageError` will be returned. - pub fn fingerprint_string( - &self, - hash: FingerprintType, - output: &mut String, - ) -> Result<(), Error> { - let mut output_size = 0; - unsafe { - s2n_tls_sys::s2n_client_hello_get_fingerprint_string( - self.deref_mut_ptr(), - hash.into(), - output.capacity() as u32, - output.as_mut_ptr(), - &mut output_size, - ) - .into_result()?; - // SAFETY: update internal state of string to match the data written - // into it. - output.as_mut_vec().set_len(output_size as usize); - }; - Ok(()) - } - - fn session_id(&self) -> Result, Error> { + pub fn session_id(&self) -> Result, Error> { let mut session_id_length = 0; unsafe { s2n_client_hello_get_session_id_length(self.deref_mut_ptr(), &mut session_id_length) @@ -168,7 +77,7 @@ impl ClientHello { Ok(session_id) } - fn server_name(&self) -> Result, Error> { + pub fn server_name(&self) -> Result, Error> { let mut server_name_length = 0; unsafe { s2n_client_hello_get_server_name_length(self.deref_mut_ptr(), &mut server_name_length) @@ -189,7 +98,7 @@ impl ClientHello { Ok(server_name) } - fn raw_message(&self) -> Result, Error> { + pub fn raw_message(&self) -> Result, Error> { let message_length = unsafe { s2n_client_hello_get_raw_message_length(self.deref_mut_ptr()).into_result()? }; @@ -206,6 +115,275 @@ impl ClientHello { } } +#[cfg(feature = "unstable-fingerprint")] +pub use self::fingerprint::*; + +// Fingerprinting is an unstable feature. This module can be removed and added +// to the client_hello module once we have settled on an implementation. +#[cfg(feature = "unstable-fingerprint")] +pub mod fingerprint { + use crate::error::{Error, Fallible}; + use s2n_tls_sys::*; + + use super::ClientHello; + + #[non_exhaustive] + #[derive(Copy, Clone)] + pub enum FingerprintType { + JA3, + } + + // this is the size of the MD5 hash digest that is used for the JA3 fingerprint + const MD5_HASH_SIZE: u32 = 16; + + impl From for s2n_tls_sys::s2n_fingerprint_type::Type { + fn from(value: FingerprintType) -> Self { + match value { + FingerprintType::JA3 => s2n_tls_sys::s2n_fingerprint_type::FINGERPRINT_JA3, + } + } + } + + impl ClientHello { + /// `fingerprint_hash` calculates the hash, and also returns the size + /// required for the full fingerprint string. The return value can be used + /// to construct a string of appropriate capacity to call + /// `fingerprint_string`. `output` will be extended if necessary to store + /// the full hash. + /// + /// ```no_run + /// use s2n_tls::client_hello::{ClientHello, FingerprintType}; + /// use s2n_tls::connection::Connection; + /// use s2n_tls::enums::Mode; + /// + /// let mut conn = Connection::new(Mode::Server); + /// // handshake happens + /// let mut client_hello: &ClientHello = conn.client_hello().unwrap(); + /// let mut hash = Vec::new(); + /// let string_size = client_hello.fingerprint_hash(FingerprintType::JA3, &mut hash).unwrap(); + /// // hash has been resized so that it can store the fingerprint hash + /// + /// let mut string = String::with_capacity(string_size as usize); + /// // string will not be resized, and the method will fail with + /// // ErrorType::UsageError if the string doesn't have enough capacity + /// client_hello.fingerprint_string(FingerprintType::JA3, &mut string).unwrap(); + /// ``` + pub fn fingerprint_hash( + &self, + hash: FingerprintType, + output: &mut Vec, + ) -> Result { + let mut hash_size: u32 = 0; + let mut str_size: u32 = 0; + // make sure the vec has sufficient space for the hash + if output.capacity() < MD5_HASH_SIZE as usize { + output.reserve_exact(MD5_HASH_SIZE as usize - output.len()); + } + unsafe { + s2n_client_hello_get_fingerprint_hash( + self.deref_mut_ptr(), + hash.into(), + MD5_HASH_SIZE, + output.as_mut_ptr(), + &mut hash_size, + &mut str_size, + ) + .into_result()?; + // SAFETY: we wrote to the raw vec (using the mut pointer), and need + // to update the state of the vec to reflect the changes we made. + output.set_len(hash_size as usize); + }; + Ok(str_size) + } + + /// `fingerprint_string` will try to calculate the fingerprint and store the + /// resulting string in `output`. If `output` does not have sufficient + /// capacity an Error of `ErrorType::UsageError` will be returned. + pub fn fingerprint_string( + &self, + hash: FingerprintType, + output: &mut String, + ) -> Result<(), Error> { + let mut output_size = 0; + unsafe { + s2n_tls_sys::s2n_client_hello_get_fingerprint_string( + self.deref_mut_ptr(), + hash.into(), + output.capacity() as u32, + output.as_mut_ptr(), + &mut output_size, + ) + .into_result()?; + // SAFETY: update internal state of string to match the data written + // into it. + output.as_mut_vec().set_len(output_size as usize); + }; + Ok(()) + } + } + + #[cfg(test)] + pub mod fingerprint_tests { + use crate::{ + client_hello::{ + fingerprint::{FingerprintType, MD5_HASH_SIZE}, + ClientHello, + }, + connection::Connection, + error::{Error, ErrorType}, + security, + testing::{poll_tls_pair, tls_pair}, + }; + + /// 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 { + let config = crate::testing::config_builder(&security::DEFAULT_TLS13) + .unwrap() + .build() + .unwrap(); + let pair = tls_pair(config); + let pair = poll_tls_pair(pair); + // this doesn't have the handshake header + let client_hello_message = pair + .server + .0 + .connection() + .client_hello() + .unwrap() + .raw_message() + .unwrap(); + // 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 + assert!(client_hello_message.len() < u8::MAX as usize); + // tag for handshake header + client_hello[0] = 1; + client_hello[3] = client_hello_message.len() as u8; + client_hello.extend(client_hello_message.iter()); + client_hello + } + + fn known_test_case( + raw_client_hello: Vec, + expected_string: &str, + expected_hash_hex: &str, + ) -> Result<(), Error> { + let expected_hash: Vec = hex::decode(expected_hash_hex).unwrap(); + let client_hello = + ClientHello::parse_client_hello(raw_client_hello.as_slice()).unwrap(); + + let mut hash = Vec::new(); + let string_size = client_hello + .fingerprint_hash(FingerprintType::JA3, &mut hash) + .unwrap(); + assert_eq!(hash, expected_hash); + + let mut string = String::with_capacity(string_size as usize); + client_hello + .fingerprint_string(FingerprintType::JA3, &mut string) + .unwrap(); + assert_eq!(string, expected_string); + Ok(()) + } + + 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() + } + + pub fn client_hello_bytes() -> Vec { + vec![ + 0x01, 0x00, 0x00, 0xEC, 0x03, 0x03, 0x90, 0xe8, 0xcc, 0xee, 0xe5, 0x70, 0xa2, 0xa1, + 0x2f, 0x6b, 0x69, 0xd2, 0x66, 0x96, 0x0f, 0xcf, 0x20, 0xd5, 0x32, 0x6e, 0xc4, 0xb2, + 0x8c, 0xc7, 0xbd, 0x0a, 0x06, 0xc2, 0xa5, 0x14, 0xfc, 0x34, 0x20, 0xaf, 0x72, 0xbf, + 0x39, 0x99, 0xfb, 0x20, 0x70, 0xc3, 0x10, 0x83, 0x0c, 0xee, 0xfb, 0xfa, 0x72, 0xcc, + 0x5d, 0xa8, 0x99, 0xb4, 0xc5, 0x53, 0xd6, 0x3d, 0xa0, 0x53, 0x7a, 0x5c, 0xbc, 0xf5, + 0x0b, 0x00, 0x1e, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0, + 0x30, 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33, 0x00, 0x39, 0x00, + 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x23, 0x00, + 0x21, 0x00, 0x00, 0x1e, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x6d, 0x6f, 0x7a, 0x69, 0x6c, + 0x6c, 0x61, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, + 0x31, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, + 0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, + 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00, 0x1c, 0x00, 0x02, + 0x40, 0x00, + ] + } + + // test that a fingerprint can successfully be calculated from ClientHellos + // returned from a connection + #[checkers::test] + fn io_fingerprint_test() { + let config = crate::testing::config_builder(&security::DEFAULT_TLS13) + .unwrap() + .build() + .unwrap(); + let pair = crate::testing::tls_pair(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!(check_client_hello(server_conn).is_ok()); + assert!(check_client_hello(client_conn).is_ok()); + } + + // known value test case copied from s2n_fingerprint_ja3_test.c + #[checkers::test] + fn valid_client_bytes() { + let raw_client_hello = client_hello_bytes(); + let expected_fingerprint = "771,49195-49199-52393-52392-49196-49200-\ + 49162-49161-49171-49172-51-57-47-53-10,0-\ + 23-65281-10-11-35-16-5-13-28,29-23-24-25,0"; + let expected_hash_hex = "839bbe3ed07fed922ded5aaf714d6842"; + known_test_case(raw_client_hello, expected_fingerprint, expected_hash_hex).unwrap(); + } + + #[test] + fn hash_output_resizing() { + let client_hello = get_client_hello(); + let hash_capacities = vec![0, MD5_HASH_SIZE, 1_000]; + for initial_size in hash_capacities { + let mut hash = Vec::with_capacity(initial_size as usize); + client_hello + .fingerprint_hash(FingerprintType::JA3, &mut hash) + .unwrap(); + assert_eq!(hash.len(), MD5_HASH_SIZE as usize); + } + } + + #[test] + fn string_output_too_small() { + let client_hello = get_client_hello(); + let mut fingerprint_string = String::with_capacity(0); + let fingerprint_err = client_hello + .fingerprint_string(FingerprintType::JA3, &mut fingerprint_string) + .unwrap_err(); + assert_eq!(fingerprint_err.kind(), ErrorType::UsageError); + } + } +} + impl Drop for ClientHello { fn drop(&mut self) { let mut client_hello: *mut s2n_client_hello = &mut self.0; @@ -222,115 +400,16 @@ impl fmt::Debug for ClientHello { let session_id = self.session_id().map_err(|_| fmt::Error)?; let session_id = hex::encode(session_id); let message_head = self.raw_message().map_err(|_| fmt::Error)?; - let mut hash = Vec::new(); - self.fingerprint_hash(FingerprintType::JA3, &mut hash) - .map_err(|_| fmt::Error)?; f.debug_struct("ClientHello") .field("session_id", &session_id) .field("message_len", &(message_head.len())) - .field("ja3_fingerprint", &hex::encode(hash)) .finish_non_exhaustive() } } #[cfg(test)] mod tests { - use super::*; - use crate::{ - connection::Connection, - error::{Error, ErrorType}, - security, - testing::{poll_tls_pair, tls_pair}, - }; - - /// 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 { - let config = crate::testing::config_builder(&security::DEFAULT_TLS13) - .unwrap() - .build() - .unwrap(); - let pair = tls_pair(config); - let pair = poll_tls_pair(pair); - // this doesn't have the handshake header - let client_hello_message = pair - .server - .0 - .connection() - .client_hello() - .unwrap() - .raw_message() - .unwrap(); - // 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 - assert!(client_hello_message.len() < u8::MAX as usize); - // tag for handshake header - client_hello[0] = 1; - client_hello[3] = client_hello_message.len() as u8; - client_hello.extend(client_hello_message.iter()); - client_hello - } - - 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() - } - - // test that a fingerprint can successfully be calculated from ClientHellos - // returned from a connection - #[checkers::test] - fn io_fingerprint_test() { - let config = crate::testing::config_builder(&security::DEFAULT_TLS13) - .unwrap() - .build() - .unwrap(); - let pair = crate::testing::tls_pair(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!(check_client_hello(server_conn).is_ok()); - assert!(check_client_hello(client_conn).is_ok()); - } - - fn known_test_case( - raw_client_hello: Vec, - expected_string: &str, - expected_hash_hex: &str, - ) -> Result<(), Error> { - let expected_hash: Vec = hex::decode(expected_hash_hex).unwrap(); - let client_hello = ClientHello::parse_client_hello(raw_client_hello.as_slice()).unwrap(); - - let mut hash = Vec::new(); - let string_size = client_hello - .fingerprint_hash(FingerprintType::JA3, &mut hash) - .unwrap(); - assert_eq!(hash, expected_hash); - - let mut string = String::with_capacity(string_size as usize); - client_hello - .fingerprint_string(FingerprintType::JA3, &mut string) - .unwrap(); - assert_eq!(string, expected_string); - Ok(()) - } + use crate::client_hello::ClientHello; #[test] fn invalid_client_bytes() { @@ -340,9 +419,8 @@ mod tests { assert!(result.is_err()); } - // known value test case copied from s2n_fingerprint_ja3_test.c - #[checkers::test] - fn valid_client_bytes() { + #[test] + fn server_name() { let raw_client_hello = vec![ 0x01, 0x00, 0x00, 0xEC, 0x03, 0x03, 0x90, 0xe8, 0xcc, 0xee, 0xe5, 0x70, 0xa2, 0xa1, 0x2f, 0x6b, 0x69, 0xd2, 0x66, 0x96, 0x0f, 0xcf, 0x20, 0xd5, 0x32, 0x6e, 0xc4, 0xb2, @@ -363,45 +441,8 @@ mod tests { 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x00, ]; - let expected_fingerprint = "771,49195-49199-52393-52392-49196-49200-\ - 49162-49161-49171-49172-51-57-47-53-10,0-\ - 23-65281-10-11-35-16-5-13-28,29-23-24-25,0"; - let expected_hash_hex = "839bbe3ed07fed922ded5aaf714d6842"; - known_test_case(raw_client_hello, expected_fingerprint, expected_hash_hex).unwrap(); - } - - #[test] - fn hash_output_resizing() { - let client_hello = get_client_hello(); - let hash_capacities = vec![0, MD5_HASH_SIZE, 1_000]; - for initial_size in hash_capacities { - let mut hash = Vec::with_capacity(initial_size as usize); - client_hello - .fingerprint_hash(FingerprintType::JA3, &mut hash) - .unwrap(); - assert_eq!(hash.len(), MD5_HASH_SIZE as usize); - } - } - - #[test] - fn string_output_too_small() { - let client_hello = get_client_hello(); - let mut fingerprint_string = String::with_capacity(0); - let fingerprint_err = client_hello - .fingerprint_string(FingerprintType::JA3, &mut fingerprint_string) - .unwrap_err(); - assert_eq!(fingerprint_err.kind(), ErrorType::UsageError); - } - - // make sure that debug doesn't panic and seems reasonable - #[test] - fn debug() { - let client_hello = get_client_hello(); - let mut hash = Vec::new(); - client_hello - .fingerprint_hash(FingerprintType::JA3, &mut hash) - .unwrap(); - let client_hello_debug = format!("{:?}", client_hello); - assert!(client_hello_debug.contains(&hex::encode(hash))); + let client_hello = ClientHello::parse_client_hello(raw_client_hello.as_slice()).unwrap(); + let server_name = client_hello.server_name().unwrap(); + assert_eq!("incoming.telemetry.mozilla.org".as_bytes(), server_name); } } diff --git a/bindings/rust/s2n-tls/src/config.rs b/bindings/rust/s2n-tls/src/config.rs index 9c42ad2b532..394ee349ccc 100644 --- a/bindings/rust/s2n-tls/src/config.rs +++ b/bindings/rust/s2n-tls/src/config.rs @@ -36,6 +36,14 @@ impl Config { /// Returns a Config object with pre-defined defaults. /// /// Use the [`Builder`] if custom configuration is desired. + /// + /// # Warning + /// + /// The newly created Config will use the default security policy. + /// Consider changing this depending on your security and compatibility requirements + /// by using [`Builder`] and [`Builder::set_security_policy`]. + /// See the s2n-tls usage guide: + /// pub fn new() -> Self { Self::default() } @@ -158,6 +166,13 @@ pub struct Builder { } impl Builder { + /// # Warning + /// + /// The newly created Builder will create Configs that use the default security policy. + /// Consider changing this depending on your security and compatibility requirements + /// by calling [`Builder::set_security_policy`]. + /// See the s2n-tls usage guide: + /// pub fn new() -> Self { crate::init::init(); let config = unsafe { s2n_config_new_minimal().into_result() }.unwrap(); @@ -719,6 +734,18 @@ impl Builder { Ok(self) } + /// Sets the expected connection serialization version. Must be set + /// before serializing the connection. + pub fn set_serialization_version( + &mut self, + version: SerializationVersion, + ) -> Result<&mut Self, Error> { + unsafe { + s2n_config_set_serialization_version(self.as_mut_ptr(), version.into()).into_result() + }?; + Ok(self) + } + pub fn build(mut self) -> Result { if self.load_system_certs { unsafe { @@ -742,6 +769,13 @@ impl Builder { } } +/// # Warning +/// +/// The newly created Builder uses the default security policy. +/// Consider changing this depending on your security and compatibility requirements +/// by using [`Builder::new`] instead and calling [`Builder::set_security_policy`]. +/// See the s2n-tls usage guide: +/// impl Default for Builder { fn default() -> Self { Self::new() diff --git a/bindings/rust/s2n-tls/src/connection.rs b/bindings/rust/s2n-tls/src/connection.rs index 48e5ce9fdb5..2b7d636ad81 100644 --- a/bindings/rust/s2n-tls/src/connection.rs +++ b/bindings/rust/s2n-tls/src/connection.rs @@ -36,6 +36,15 @@ macro_rules! static_const_str { }; } +#[non_exhaustive] +#[derive(Debug, PartialEq)] +/// s2n-tls only tracks up to u8::MAX (255) key updates. If any of the fields show +/// 255 updates, then more than 255 updates may have occurred. +pub struct KeyUpdateCount { + pub send_key_updates: u8, + pub recv_key_updates: u8, +} + pub struct Connection { connection: NonNull, } @@ -80,6 +89,15 @@ unsafe impl Send for Connection {} unsafe impl Sync for Connection {} impl Connection { + /// # Warning + /// + /// The newly created connection uses the default security policy. + /// Consider changing this depending on your security and compatibility requirements + /// by calling [`Connection::set_security_policy`]. + /// Alternatively, you can use [`crate::config::Builder`], [`crate::config::Builder::set_security_policy`], + /// and [`Connection::set_config`] to set the policy on the Config instead of on the Connection. + /// See the s2n-tls usage guide: + /// pub fn new(mode: Mode) -> Self { crate::init::init(); @@ -267,6 +285,46 @@ impl Connection { Ok(self) } + /// Signals the connection to do a key_update at the next possible opportunity. + /// Note that the resulting key update message will not be sent until `send` is + /// called on the connection. + /// + /// `peer_request` indicates if a key update should also be requested + /// of the peer. When set to `KeyUpdateNotRequested`, then only the sending + /// key of the connection will be updated. If set to `KeyUpdateRequested`, then + /// the sending key of conn will be updated AND the peer will be requested to + /// update their sending key. Note that s2n-tls currently only supports + /// `peer_request` being set to `KeyUpdateNotRequested` and will return an error + /// if any other value is used. + pub fn request_key_update(&mut self, peer_request: PeerKeyUpdate) -> Result<&mut Self, Error> { + unsafe { + s2n_connection_request_key_update(self.connection.as_ptr(), peer_request.into()) + .into_result() + }?; + Ok(self) + } + + /// Reports the number of times sending and receiving keys have been updated. + /// + /// This only applies to TLS1.3. Earlier versions do not support key updates. + #[cfg(feature = "unstable-ktls")] + pub fn key_update_counts(&self) -> Result { + let mut send_key_updates = 0; + let mut recv_key_updates = 0; + unsafe { + s2n_connection_get_key_update_counts( + self.connection.as_ptr(), + &mut send_key_updates, + &mut recv_key_updates, + ) + .into_result()?; + } + Ok(KeyUpdateCount { + send_key_updates, + recv_key_updates, + }) + } + /// sets the application protocol preferences on an s2n_connection object. /// /// protocols is a list in order of preference, with most preferred protocol first, and of @@ -728,31 +786,28 @@ impl Connection { // /// Returns a reference to the ClientHello associated with the connection. /// ```compile_fail - /// use s2n_tls::client_hello::{ClientHello, FingerprintType}; + /// use s2n_tls::client_hello::ClientHello; /// use s2n_tls::connection::Connection; /// use s2n_tls::enums::Mode; /// /// let mut conn = Connection::new(Mode::Server); /// let mut client_hello: &ClientHello = conn.client_hello().unwrap(); - /// let mut hash = Vec::new(); /// drop(conn); - /// client_hello.fingerprint_hash(FingerprintType::JA3, &mut hash); + /// client_hello.raw_message(); /// ``` /// /// The compilation could be failing for a variety of reasons, so make sure /// that the test case is actually good. /// ```no_run - /// use s2n_tls::client_hello::{ClientHello, FingerprintType}; + /// use s2n_tls::client_hello::ClientHello; /// use s2n_tls::connection::Connection; /// use s2n_tls::enums::Mode; /// /// let mut conn = Connection::new(Mode::Server); /// let mut client_hello: &ClientHello = conn.client_hello().unwrap(); - /// let mut hash = Vec::new(); - /// client_hello.fingerprint_hash(FingerprintType::JA3, &mut hash); + /// client_hello.raw_message(); /// drop(conn); /// ``` - #[cfg(feature = "unstable-fingerprint")] pub fn client_hello(&self) -> Result<&crate::client_hello::ClientHello, Error> { let mut handle = unsafe { s2n_connection_get_client_hello(self.connection.as_ptr()).into_result()? }; @@ -921,6 +976,47 @@ impl Connection { } Ok(secret) } + + /// Retrieves the size of the serialized connection + pub fn serialization_length(&self) -> Result { + unsafe { + let mut length = 0; + s2n_connection_serialization_length(self.connection.as_ptr(), &mut length) + .into_result()?; + Ok(length.try_into().unwrap()) + } + } + + /// Serializes the TLS connection into the provided buffer + pub fn serialize(&self, output: &mut [u8]) -> Result<(), Error> { + unsafe { + s2n_connection_serialize( + self.connection.as_ptr(), + output.as_mut_ptr(), + output.len().try_into().map_err(|_| Error::INVALID_INPUT)?, + ) + .into_result()?; + Ok(()) + } + } + + /// Deserializes the input buffer into a new TLS connection that can send/recv + /// data from the original peer. + pub fn deserialize(&mut self, input: &[u8]) -> Result<(), Error> { + let size = input.len(); + /* This is not ideal, we know that s2n_connection_deserialize will not mutate the + * input value, however, the mut is needed to use the stuffer functions. */ + let input = input.as_ptr() as *mut u8; + unsafe { + s2n_connection_deserialize( + self.as_ptr(), + input, + size.try_into().map_err(|_| Error::INVALID_INPUT)?, + ) + .into_result()?; + Ok(()) + } + } } struct Context { diff --git a/bindings/rust/s2n-tls/src/enums.rs b/bindings/rust/s2n-tls/src/enums.rs index 5bc32d47b14..d642a7630ce 100644 --- a/bindings/rust/s2n-tls/src/enums.rs +++ b/bindings/rust/s2n-tls/src/enums.rs @@ -177,3 +177,35 @@ impl TryFrom for HashAlgorithm { Ok(version) } } + +#[non_exhaustive] +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum PeerKeyUpdate { + KeyUpdateNotRequested, + KeyUpdatedRequested, +} + +impl From for s2n_peer_key_update::Type { + fn from(input: PeerKeyUpdate) -> s2n_peer_key_update::Type { + match input { + PeerKeyUpdate::KeyUpdateNotRequested => s2n_peer_key_update::KEY_UPDATE_NOT_REQUESTED, + PeerKeyUpdate::KeyUpdatedRequested => s2n_peer_key_update::KEY_UPDATE_REQUESTED, + } + } +} + +#[non_exhaustive] +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum SerializationVersion { + None, + V1, +} + +impl From for s2n_serialization_version::Type { + fn from(input: SerializationVersion) -> s2n_serialization_version::Type { + match input { + SerializationVersion::None => s2n_serialization_version::SERIALIZED_CONN_NONE, + SerializationVersion::V1 => s2n_serialization_version::SERIALIZED_CONN_V1, + } + } +} diff --git a/bindings/rust/s2n-tls/src/lib.rs b/bindings/rust/s2n-tls/src/lib.rs index 78b4e81c572..40bbb6dd1ff 100644 --- a/bindings/rust/s2n-tls/src/lib.rs +++ b/bindings/rust/s2n-tls/src/lib.rs @@ -15,7 +15,6 @@ pub mod error; pub mod callbacks; pub mod cert_chain; -#[cfg(feature = "unstable-fingerprint")] pub mod client_hello; pub mod config; pub mod connection; diff --git a/bindings/rust/s2n-tls/src/security.rs b/bindings/rust/s2n-tls/src/security.rs index 8ee9a61be3d..e3684a67336 100644 --- a/bindings/rust/s2n-tls/src/security.rs +++ b/bindings/rust/s2n-tls/src/security.rs @@ -1,6 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +//! Security options like cipher suites, signature algorithms, versions, etc. +//! +//! See + use crate::error::Error; use core::fmt; use std::ffi::{CStr, CString}; @@ -25,6 +29,8 @@ impl Policy { } } + /// See the s2n-tls usage guide for details on available policies: + /// pub fn from_version(version: &str) -> Result { let cstr = CString::new(version).map_err(|_| Error::INVALID_INPUT)?; let context = Context::Owned(cstr); @@ -39,16 +45,47 @@ impl fmt::Debug for Policy { } macro_rules! policy { - ($name:ident, $version:expr) => { - pub const $name: Policy = Policy(Context::Static(concat!($version, "\0").as_bytes())); + ($version:expr) => { + Policy(Context::Static(concat!($version, "\0").as_bytes())) }; } -policy!(DEFAULT, "default"); -policy!(DEFAULT_TLS13, "default_tls13"); +/// Default policy +/// +/// # Warning +/// +/// Cipher suites, curves, signature algorithms, or other security policy options +/// may be added or removed from "default" in order to keep it up to date with +/// current security best practices. +/// +/// That means that updating the library may cause the policy to change. If peers +/// are expected to be reasonably modern and support standard options, then this +/// should not be a problem. But if peers rely on a deprecated option that is removed, +/// they may be unable to connect. +/// +/// If you instead need a static, versioned policy, choose one according to the s2n-tls usage guide: +/// +pub const DEFAULT: Policy = policy!("default"); + +/// Default policy supporting TLS1.3 +/// +/// # Warning +/// +/// Cipher suites, curves, signature algorithms, or other security policy options +/// may be added or removed from "default_tls13" in order to keep it up to date with +/// current security best practices. +/// +/// That means that updating the library may cause the policy to change. If peers +/// are expected to be reasonably modern and support standard options, then this +/// should not be a problem. But if peers rely on a deprecated option that is removed, +/// they may be unable to connect. +/// +/// If you instead need a static, versioned policy, choose one according to the s2n-tls usage guide: +/// +pub const DEFAULT_TLS13: Policy = policy!("default_tls13"); #[cfg(feature = "pq")] -policy!(TESTING_PQ, "PQ-TLS-1-0-2021-05-26"); +pub const TESTING_PQ: Policy = policy!("PQ-TLS-1-0-2021-05-26"); pub const ALL_POLICIES: &[Policy] = &[ DEFAULT, diff --git a/bindings/rust/s2n-tls/src/testing/s2n_tls.rs b/bindings/rust/s2n-tls/src/testing/s2n_tls.rs index 3b5c22b98ca..962370e57e3 100644 --- a/bindings/rust/s2n-tls/src/testing/s2n_tls.rs +++ b/bindings/rust/s2n-tls/src/testing/s2n_tls.rs @@ -925,4 +925,40 @@ mod tests { Ok(()) } + + #[cfg(feature = "unstable-ktls")] + #[test] + fn key_updates() -> Result<(), Error> { + use crate::{connection::KeyUpdateCount, enums::PeerKeyUpdate}; + + let empty_key_updates = KeyUpdateCount { + recv_key_updates: 0, + send_key_updates: 0, + }; + + let pair = tls_pair(build_config(&security::DEFAULT_TLS13)?); + let mut pair = poll_tls_pair(pair); + + // there haven't been any key updates at the start of the connection + let client_updates = pair.client.0.connection.as_ref().key_update_counts()?; + assert_eq!(client_updates, empty_key_updates); + let server_updates = pair.server.0.connection.as_ref().key_update_counts()?; + assert_eq!(server_updates, empty_key_updates); + + pair.server + .0 + .connection + .as_mut() + .request_key_update(PeerKeyUpdate::KeyUpdateNotRequested)?; + assert!(pair.poll_send(Mode::Server, &[0]).is_ready()); + + // the server send key has been updated + let client_updates = pair.client.0.connection.as_ref().key_update_counts()?; + assert_eq!(client_updates, empty_key_updates); + let server_updates = pair.server.0.connection.as_ref().key_update_counts()?; + assert_eq!(server_updates.recv_key_updates, 0); + assert_eq!(server_updates.send_key_updates, 1); + + Ok(()) + } } diff --git a/codebuild/spec/buildspec_asan.yml b/codebuild/spec/buildspec_asan.yml index d6ea0e4cb0e..ecc68746522 100644 --- a/codebuild/spec/buildspec_asan.yml +++ b/codebuild/spec/buildspec_asan.yml @@ -18,43 +18,93 @@ version: 0.2 # Codebuild's matrix jobs have non-differentiated names so use batch-list # instead. + +# Parameter motivation + +# COMPILERS +# We run asan on both gcc and clang because of different features sets for their +# address sanitizers. Specifically there was a case where GCC was able to detect +# a memcpy-param-overlap that Clang did not. + +# LIBCRYPTOS +# awslc: happy path libcrypto for s2n-tls +# openssl 3: s2n-tls takes different code paths for ossl3, so make sure we run +# asan on it. See pr 4033 for a historical motivating example. +# openssl 1.1.1: a widely deployed version of openssl. +# openssl 1.0.2: the default libcrypto on AL2, and AL2 is still widely deployed. + +# CMAKE_BUILD_TYPE +# RelWithDebInfo: This instructs CMake to do all optimizations (Rel -> Release) +# along with debug info (DebInfo). Debug info is necessary to get line numbers +# in the stack traces that ASAN reports. batch: build-list: - # awslc is the happy path libcrypto for s2n-tls - - identifier: awslc + - identifier: clang_awslc + env: + compute-type: BUILD_GENERAL1_LARGE + variables: + S2N_LIBCRYPTO: awslc + COMPILER: clang + - identifier: clang_openssl_3_0 + env: + compute-type: BUILD_GENERAL1_LARGE + variables: + S2N_LIBCRYPTO: openssl-3.0 + COMPILER: clang + - identifier: clang_openssl_1_1_1 + env: + compute-type: BUILD_GENERAL1_LARGE + variables: + S2N_LIBCRYPTO: openssl-1.1.1 + COMPILER: clang + - identifier: clang_openssl_1_0_2 + env: + compute-type: BUILD_GENERAL1_LARGE + variables: + S2N_LIBCRYPTO: openssl-1.0.2 + COMPILER: clang + - identifier: gcc_awslc env: compute-type: BUILD_GENERAL1_LARGE variables: S2N_LIBCRYPTO: awslc - # s2n-tls takes different code paths for ossl3, so make sure we run asan on - # it. See pr 4033 for a historical motivating example. - - identifier: openssl_3_0 + COMPILER: gcc + - identifier: gcc_openssl_3_0 env: compute-type: BUILD_GENERAL1_LARGE variables: S2N_LIBCRYPTO: openssl-3.0 - # openssl 1.1.1 is a widely deployed version of openssl. - - identifier: openssl_1_1_1 + COMPILER: gcc + - identifier: gcc_openssl_1_1_1 env: compute-type: BUILD_GENERAL1_LARGE variables: S2N_LIBCRYPTO: openssl-1.1.1 - # openssl 1.0.2 is the default distributed on AL2, and AL2 is still widely - # deployed - - identifier: openssl_1_0_2 + COMPILER: gcc + - identifier: gcc_openssl_1_0_2 env: compute-type: BUILD_GENERAL1_LARGE variables: S2N_LIBCRYPTO: openssl-1.0.2 + COMPILER: gcc phases: + pre_build: + commands: + - | + if [ -d "third-party-src" ]; then + cd third-party-src; + ln -s /usr/local $CODEBUILD_SRC_DIR/third-party-src/test-deps; + fi + - /usr/bin/$COMPILER --version build: on-failure: ABORT commands: - | cmake . -Bbuild \ - -DCMAKE_C_COMPILER=/usr/bin/clang \ + -DCMAKE_C_COMPILER=/usr/bin/$COMPILER \ -DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DASAN=ON - cmake --build ./build -- -j $(nproc) post_build: diff --git a/docs/usage-guide/topics/ch06-security-policies.md b/docs/usage-guide/topics/ch06-security-policies.md index 2eea2376080..a4e5b753ca4 100644 --- a/docs/usage-guide/topics/ch06-security-policies.md +++ b/docs/usage-guide/topics/ch06-security-policies.md @@ -16,6 +16,7 @@ The following chart maps the security policy version to protocol version and cip | version | TLS1.0 | TLS1.1 | TLS1.2 | TLS1.3 | AES-CBC | AES-GCM | CHACHAPOLY | 3DES | RC4 | DHE | ECDHE | RSA kx | |---------------|--------|--------|--------|--------|---------|---------|------------|------|-----|-----|-------|--------| | 20230317 | | | X | X | X | X | | | | | X | | +| 20240331 | | | X | | X | X | | | | | X | | | default | X | X | X | | X | X | X | | | | X | X | | default_tls13 | X | X | X | X | X | X | X | | | | X | X | | default_fips | | | X | | X | X | | | | X | X | | @@ -39,24 +40,29 @@ The following chart maps the security policy version to protocol version and cip | 20200207 | | | | X | | X | X | | | | X | | | rfc9151 | | | X | X | | X | | | | X | X | X | -The "default", "default_tls13", and "default_fips" versions are special in that they will be updated with future s2n-tls changes and ciphersuites and protocol versions may be added and removed, or their internal order of preference might change. Numbered versions are fixed and will never change. -In general, customers prefer to use numbered versions for production use cases to prevent impact from library updates. +The "default", "default_tls13", and "default_fips" versions are special in that they will be updated with future s2n-tls changes to keep up-to-date with current security best practices. Ciphersuites, protocol versions, and other options may be added or removed, or their internal order of preference might change. **Warning**: this means that the default policies may change as a result of library updates, which could break peers that rely on legacy options. -"20230317" is a FIPS compliant policy. It offers more limited but more secure options than "default". It only supports TLS1.2 and TLS1.3. Consider this policy if you plan to enable FIPS mode or don't need or want to support less secure legacy options like TLS1.1 or SHA1. +In contrast, numbered or dated versions are fixed and will never change. The numbered equivalents of the default policies are currently: +* "default": "20170210" +* "default_tls13": "20240417" +* "default_fips": "20240416" + +"20230317" offers more limited but more secure options than the default policies. Consider it if you don't need or want to support less secure legacy options like TLS1.1 or SHA1. It is also FIPS compliant and supports TLS1.3. If you need a version of this policy that doesn't support TLS1.3, choose "20240331" instead. "20160411" follows the same general preference order as "default". The main difference is it has a CBC cipher suite at the top. This is to accommodate certain Java clients that have poor GCM implementations. Users of s2n-tls who have found GCM to be hurting performance for their clients should consider this version. "rfc9151" is derived from [Commercial National Security Algorithm (CNSA) Suite Profile for TLS and DTLS 1.2 and 1.3](https://datatracker.ietf.org/doc/html/rfc9151). This policy restricts the algorithms allowed for signatures on certificates in the certificate chain to RSA or ECDSA with sha384, which may require you to update your certificates. +Like the default policies, this policy may also change if the source RFC definition changes. s2n-tls does not expose an API to control the order of preference for each ciphersuite or protocol version. s2n-tls follows the following order: -*NOTE*: All ChaCha20-Poly1305 cipher suites will not be available if s2n-tls is not built with an Openssl 1.1.1 libcrypto. The underlying encrypt/decrypt functions are not available in older versions. - 1. Always prefer the highest protocol version supported 2. Always use forward secrecy where possible. Prefer ECDHE over DHE. 3. Prefer encryption ciphers in the following order: AES128, AES256, ChaCha20, 3DES, RC4. 4. Prefer record authentication modes in the following order: GCM, Poly1305, SHA256, SHA1, MD5. +*NOTE*: ChaCha20-Poly1305 cipher suites will not be available if s2n-tls is built with an older libcrypto like openssl-1.0.2. The underlying encrypt/decrypt functions are not available in older versions. + #### ChaCha20 Boosting s2n-tls usually prefers AES over ChaCha20. However, some clients-- particularly mobile or IOT devices-- do not support AES hardware acceleration, making AES less efficient and performant than ChaCha20. In this case, clients will indicate their preference for ChaCha20 by listing it first during cipher suite negotiation. Usually s2n-tls servers ignore client preferences, but s2n-tls offers "ChaCha20 boosted" security policies that will choose ChaCha20 over AES if the client indicates a preference for ChaCha20. This is available in the "CloudFront-TLS-1-2-2021-ChaCha20-Boosted" policy, which is identical to the "CloudFront-TLS-1-2-2021" policy listed above but with ChaCha20 Boosting enabled. @@ -89,7 +95,9 @@ s2n-tls usually prefers AES over ChaCha20. However, some clients-- particularly | 20200207 | | X | | X | | rfc9151 | X | X | | X | -Note that legacy SHA-1 algorithms are not supported in TLS1.3. Legacy SHA-1 algorithms will be supported only if TLS1.2 has been negotiated and the security policy allows them. +*NOTE*: Legacy SHA-1 algorithms are not supported in TLS1.3. Legacy SHA-1 algorithms will be supported only if TLS1.2 has been negotiated and the security policy allows them. + +*NOTE*: RSA-PSS certificates will not be available if s2n-tls is built with an older libcrypto like openssl-1.0.2. RSA certificate signatures with PSS padding (RSA-PSS-RSAE) will still be available, but RSA-PSS certificate signatures with PSS padding (RSA-PSS-PSS) will not be available. ### Chart: Security policy version to supported curves/groups diff --git a/error/s2n_errno.c b/error/s2n_errno.c index 7b770681758..2f64bd5f2fe 100644 --- a/error/s2n_errno.c +++ b/error/s2n_errno.c @@ -307,7 +307,10 @@ static const char *no_such_error = "Internal s2n error"; ERR_ENTRY(S2N_ERR_KTLS_RENEG, "kTLS does not support secure renegotiation") \ ERR_ENTRY(S2N_ERR_KTLS_KEYUPDATE, "Received KeyUpdate from peer, but kernel does not support updating tls keys") \ ERR_ENTRY(S2N_ERR_KTLS_KEY_LIMIT, "Reached key encryption limit, but kernel does not support updating tls keys") \ - ERR_ENTRY(S2N_ERR_UNEXPECTED_CERT_REQUEST, "Client does not support mutual authentication") \ + ERR_ENTRY(S2N_ERR_UNEXPECTED_CERT_REQUEST, "Client forbids mutual authentication, but server requested a cert") \ + ERR_ENTRY(S2N_ERR_MISSING_CERT_REQUEST, "Client requires mutual authentication, but server did not request a cert") \ + ERR_ENTRY(S2N_ERR_MISSING_CLIENT_CERT, "Server requires client certificate") \ + ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_CONNECTION, "Serialized connection is invalid"); \ /* clang-format on */ #define ERR_STR_CASE(ERR, str) \ diff --git a/error/s2n_errno.h b/error/s2n_errno.h index fa16387839d..ced98ecc5f5 100644 --- a/error/s2n_errno.h +++ b/error/s2n_errno.h @@ -72,6 +72,8 @@ typedef enum { S2N_ERR_DECRYPT, S2N_ERR_BAD_MESSAGE, S2N_ERR_UNEXPECTED_CERT_REQUEST, + S2N_ERR_MISSING_CERT_REQUEST, + S2N_ERR_MISSING_CLIENT_CERT, S2N_ERR_KEY_INIT, S2N_ERR_KEY_DESTROY, S2N_ERR_DH_SERIALIZING, @@ -325,6 +327,7 @@ typedef enum { S2N_ERR_ATOMIC, S2N_ERR_KTLS_KEY_LIMIT, S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT, + S2N_ERR_INVALID_SERIALIZED_CONNECTION, S2N_ERR_T_USAGE_END, } s2n_error; diff --git a/stuffer/s2n_stuffer.c b/stuffer/s2n_stuffer.c index 6e6814cd54a..5820a55c69b 100644 --- a/stuffer/s2n_stuffer.c +++ b/stuffer/s2n_stuffer.c @@ -215,7 +215,7 @@ int s2n_stuffer_wipe_n(struct s2n_stuffer *stuffer, const uint32_t size) bool s2n_stuffer_is_consumed(struct s2n_stuffer *stuffer) { - return stuffer && (stuffer->read_cursor == stuffer->write_cursor); + return stuffer && (stuffer->read_cursor == stuffer->write_cursor) && !stuffer->tainted; } int s2n_stuffer_wipe(struct s2n_stuffer *stuffer) diff --git a/tests/cbmc/proofs/s2n_stuffer_is_consumed/s2n_stuffer_is_consumed_harness.c b/tests/cbmc/proofs/s2n_stuffer_is_consumed/s2n_stuffer_is_consumed_harness.c index 4ec0db70de7..16cba19b8b7 100644 --- a/tests/cbmc/proofs/s2n_stuffer_is_consumed/s2n_stuffer_is_consumed_harness.c +++ b/tests/cbmc/proofs/s2n_stuffer_is_consumed/s2n_stuffer_is_consumed_harness.c @@ -33,10 +33,13 @@ void s2n_stuffer_is_consumed_harness() save_byte_from_blob(&stuffer->blob, &old_byte_from_stuffer); /* Operation under verification. */ - if (s2n_stuffer_is_consumed(stuffer)) { - assert(stuffer->read_cursor == old_stuffer.write_cursor); + bool result = s2n_stuffer_is_consumed(stuffer); + if (old_stuffer.read_cursor != old_stuffer.write_cursor) { + assert(result == false); + } else if (old_stuffer.tainted) { + assert(result == false); } else { - assert(stuffer->read_cursor != old_stuffer.write_cursor); + assert(result == true); } /* Post-conditions. */ diff --git a/tests/sidetrail/working/s2n-record-read-cbc-negative-test/record_read_cbc.patch b/tests/sidetrail/working/s2n-record-read-cbc-negative-test/record_read_cbc.patch index 1004197bfad..ba312ffecf2 100644 --- a/tests/sidetrail/working/s2n-record-read-cbc-negative-test/record_read_cbc.patch +++ b/tests/sidetrail/working/s2n-record-read-cbc-negative-test/record_read_cbc.patch @@ -1,17 +1,17 @@ diff --git a/tls/s2n_record_read_cbc.c b/tls/s2n_record_read_cbc.c -index bb06523..60a2eed 100644 +index 346e6571..0f2527e9 100644 --- a/tls/s2n_record_read_cbc.c +++ b/tls/s2n_record_read_cbc.c -@@ -30,6 +30,8 @@ - #include "utils/s2n_safety.h" +@@ -26,6 +26,8 @@ #include "utils/s2n_blob.h" + #include "utils/s2n_safety.h" +extern int g_padding_length; + int s2n_record_parse_cbc( - const struct s2n_cipher_suite *cipher_suite, - struct s2n_connection *conn, -@@ -86,6 +88,8 @@ int s2n_record_parse_cbc( + const struct s2n_cipher_suite *cipher_suite, + struct s2n_connection *conn, +@@ -82,6 +84,8 @@ int s2n_record_parse_cbc( /* Subtract the padding length */ POSIX_ENSURE_GT(en.size, 0); @@ -20,11 +20,11 @@ index bb06523..60a2eed 100644 uint32_t out = 0; POSIX_GUARD(s2n_sub_overflow(payload_length, en.data[en.size - 1] + 1, &out)); payload_length = out; -@@ -107,6 +111,7 @@ int s2n_record_parse_cbc( +@@ -103,6 +107,7 @@ int s2n_record_parse_cbc( - /* Padding */ + /* Padding. This finalizes the provided HMAC. */ if (s2n_verify_cbc(conn, mac, &en) < 0) { + __VERIFIER_assume(0); - POSIX_GUARD(s2n_stuffer_wipe(&conn->in)); POSIX_BAIL(S2N_ERR_BAD_MESSAGE); } + diff --git a/tests/sidetrail/working/s2n-record-read-cbc/record_read_cbc.patch b/tests/sidetrail/working/s2n-record-read-cbc/record_read_cbc.patch index 1004197bfad..ba312ffecf2 100644 --- a/tests/sidetrail/working/s2n-record-read-cbc/record_read_cbc.patch +++ b/tests/sidetrail/working/s2n-record-read-cbc/record_read_cbc.patch @@ -1,17 +1,17 @@ diff --git a/tls/s2n_record_read_cbc.c b/tls/s2n_record_read_cbc.c -index bb06523..60a2eed 100644 +index 346e6571..0f2527e9 100644 --- a/tls/s2n_record_read_cbc.c +++ b/tls/s2n_record_read_cbc.c -@@ -30,6 +30,8 @@ - #include "utils/s2n_safety.h" +@@ -26,6 +26,8 @@ #include "utils/s2n_blob.h" + #include "utils/s2n_safety.h" +extern int g_padding_length; + int s2n_record_parse_cbc( - const struct s2n_cipher_suite *cipher_suite, - struct s2n_connection *conn, -@@ -86,6 +88,8 @@ int s2n_record_parse_cbc( + const struct s2n_cipher_suite *cipher_suite, + struct s2n_connection *conn, +@@ -82,6 +84,8 @@ int s2n_record_parse_cbc( /* Subtract the padding length */ POSIX_ENSURE_GT(en.size, 0); @@ -20,11 +20,11 @@ index bb06523..60a2eed 100644 uint32_t out = 0; POSIX_GUARD(s2n_sub_overflow(payload_length, en.data[en.size - 1] + 1, &out)); payload_length = out; -@@ -107,6 +111,7 @@ int s2n_record_parse_cbc( +@@ -103,6 +107,7 @@ int s2n_record_parse_cbc( - /* Padding */ + /* Padding. This finalizes the provided HMAC. */ if (s2n_verify_cbc(conn, mac, &en) < 0) { + __VERIFIER_assume(0); - POSIX_GUARD(s2n_stuffer_wipe(&conn->in)); POSIX_BAIL(S2N_ERR_BAD_MESSAGE); } + diff --git a/tests/sidetrail/working/s2n-record-read-stream/copy_as_needed.sh b/tests/sidetrail/working/s2n-record-read-stream/copy_as_needed.sh index 01db789aa3b..7a91cf994f3 100755 --- a/tests/sidetrail/working/s2n-record-read-stream/copy_as_needed.sh +++ b/tests/sidetrail/working/s2n-record-read-stream/copy_as_needed.sh @@ -43,7 +43,7 @@ mkdir -p tls #add invariants etc needed for the proof to the s2n_cbc code cp $S2N_BASE/tls/s2n_cbc.c tls/ cp $S2N_BASE/tls/s2n_record_read_stream.c tls/ -patch -p5 < ../patches/cbc.patch +patch -p1 < s2n_record_read_stream.patch mkdir -p utils cp $S2N_BASE/utils/s2n_result.c utils/ diff --git a/tests/sidetrail/working/s2n-record-read-stream/s2n_record_read_stream.patch b/tests/sidetrail/working/s2n-record-read-stream/s2n_record_read_stream.patch new file mode 100644 index 00000000000..f596255227e --- /dev/null +++ b/tests/sidetrail/working/s2n-record-read-stream/s2n_record_read_stream.patch @@ -0,0 +1,29 @@ +diff --git a/tls/s2n_record_read_stream.c b/tls/s2n_record_read_stream.c +index 927ab00f..3846cc4b 100644 +--- a/tls/s2n_record_read_stream.c ++++ b/tls/s2n_record_read_stream.c +@@ -79,15 +79,20 @@ int s2n_record_parse_stream( + POSIX_BAIL(S2N_ERR_BAD_MESSAGE); + } + ++ /* All information is declassified after the MAC is successfully verified since the record is ++ * decrypted and authenticated. Code that's executed post MAC validation need not be constant ++ * time, so it's removed from the scope of SideTrail's analysis. ++ */ ++ + /* O.k., we've successfully read and decrypted the record, now we need to align the stuffer + * for reading the plaintext data. + */ +- POSIX_GUARD(s2n_stuffer_reread(&conn->in)); +- POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); ++// POSIX_GUARD(s2n_stuffer_reread(&conn->in)); ++// POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); + + /* Truncate and wipe the MAC and any padding */ +- POSIX_GUARD(s2n_stuffer_wipe_n(&conn->in, s2n_stuffer_data_available(&conn->in) - payload_length)); +- conn->in_status = PLAINTEXT; ++// POSIX_GUARD(s2n_stuffer_wipe_n(&conn->in, s2n_stuffer_data_available(&conn->in) - payload_length)); ++// conn->in_status = PLAINTEXT; + + return 0; + } diff --git a/tests/sidetrail/working/s2n-record-read-stream/s2n_record_read_wrapper.c b/tests/sidetrail/working/s2n-record-read-stream/s2n_record_read_wrapper.c index 585d366a2db..aae292059bf 100644 --- a/tests/sidetrail/working/s2n-record-read-stream/s2n_record_read_wrapper.c +++ b/tests/sidetrail/working/s2n-record-read-stream/s2n_record_read_wrapper.c @@ -71,7 +71,7 @@ int s2n_record_parse_wrapper(int *xor_pad, uint8_t content_type ) { - __VERIFIER_ASSERT_MAX_LEAKAGE(0); + __VERIFIER_ASSERT_MAX_LEAKAGE(5); __VERIFIER_assume(encrypted_length > 0); __VERIFIER_assume(padding_length >= 0); __VERIFIER_assume(padding_length < 256); @@ -99,7 +99,7 @@ int s2n_record_parse_wrapper(int *xor_pad, struct s2n_cipher stream_cipher = { .type = S2N_STREAM, - .io.cbc.decrypt = decrypt_stream, + .io.stream.decrypt = decrypt_stream, }; struct s2n_record_algorithm record_algorithm = { diff --git a/tests/testlib/s2n_resumption_testlib.c b/tests/testlib/s2n_resumption_testlib.c new file mode 100644 index 00000000000..44686644070 --- /dev/null +++ b/tests/testlib/s2n_resumption_testlib.c @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include "testlib/s2n_testlib.h" + +S2N_RESULT s2n_resumption_test_ticket_key_setup(struct s2n_config *config) +{ + /** + *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 + *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 + *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) + **/ + S2N_RESULT_BLOB_FROM_HEX(ticket_key, + "077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5"); + + /* Set up encryption key */ + uint64_t current_time = 0; + uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + + RESULT_GUARD_POSIX(s2n_config_set_session_tickets_onoff(config, true)); + RESULT_GUARD_POSIX(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + RESULT_GUARD_POSIX(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + return S2N_RESULT_OK; +} diff --git a/tests/testlib/s2n_test_server_client.c b/tests/testlib/s2n_test_server_client.c index f4a04e5cdb2..ecaec2a752f 100644 --- a/tests/testlib/s2n_test_server_client.c +++ b/tests/testlib/s2n_test_server_client.c @@ -60,7 +60,6 @@ int s2n_negotiate_test_server_and_client(struct s2n_connection *server_conn, str S2N_RESULT s2n_negotiate_test_server_and_client_with_early_data(struct s2n_connection *server_conn, struct s2n_connection *client_conn, struct s2n_blob *early_data_to_send, struct s2n_blob *early_data_received) { - bool server_done = false, client_done = false; s2n_blocked_status blocked = S2N_NOT_BLOCKED; ssize_t total_data_sent = 0, total_data_recv = 0; ssize_t data_sent = 0, data_recv = 0; @@ -74,28 +73,37 @@ S2N_RESULT s2n_negotiate_test_server_and_client_with_early_data(struct s2n_conne * server done when it has either read all early data (which may be zero bytes) or has * rejected the early data. */ + bool server_done = false, client_done = false; + bool server_early_done = false, client_early_done = false; do { - bool client_success = (s2n_send_early_data(client_conn, early_data_to_send->data + total_data_sent, - early_data_to_send->size - total_data_sent, &data_sent, &blocked) - >= S2N_SUCCESS); - total_data_sent += data_sent; - RESULT_GUARD(s2n_validate_negotiate_result(client_success, false, &client_done)); - - bool server_success = (s2n_recv_early_data(server_conn, early_data_received->data + total_data_recv, - early_data_received->size - total_data_recv, &data_recv, &blocked) - >= S2N_SUCCESS); - total_data_recv += data_recv; - RESULT_GUARD(s2n_validate_negotiate_result(server_success, false, &server_done)); - - /* If we expect early data, then we need to keep using the early data APIs - * until we have read all the early data. - */ - server_done = (total_data_recv == total_data_sent) || (IS_NEGOTIATED(server_conn) && !WITH_EARLY_DATA(server_conn)); - } while (!client_done || !server_done); + if (!client_early_done) { + bool success = s2n_send_early_data(client_conn, + early_data_to_send->data + total_data_sent, + early_data_to_send->size - total_data_sent, + &data_sent, &blocked) + == S2N_SUCCESS; + total_data_sent += data_sent; + RESULT_GUARD(s2n_validate_negotiate_result(success, false, &client_early_done)); + } else { + bool success = (s2n_negotiate(client_conn, &blocked) == S2N_SUCCESS); + RESULT_GUARD(s2n_validate_negotiate_result(success, server_done, &client_done)); + } - /* Finish the handshake */ - RESULT_GUARD_POSIX(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + if (!server_early_done) { + bool success = s2n_recv_early_data(server_conn, + early_data_received->data + total_data_recv, + early_data_received->size - total_data_recv, + &data_recv, &blocked) + == S2N_SUCCESS; + total_data_recv += data_recv; + RESULT_GUARD(s2n_validate_negotiate_result(success, false, &server_early_done)); + } else { + bool success = (s2n_negotiate(server_conn, &blocked) == S2N_SUCCESS); + RESULT_GUARD(s2n_validate_negotiate_result(success, client_done, &server_done)); + } + } while (!client_done || !server_done); + early_data_received->size = total_data_recv; return S2N_RESULT_OK; } @@ -149,3 +157,24 @@ int s2n_shutdown_test_server_and_client(struct s2n_connection *server_conn, stru int rc = (server_rc == 0 && client_rc == 0) ? 0 : -1; return rc; } + +S2N_RESULT s2n_send_and_recv_test(struct s2n_connection *send_conn, struct s2n_connection *recv_conn) +{ + RESULT_ENSURE_REF(send_conn); + RESULT_ENSURE_REF(recv_conn); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + const uint8_t send_data[] = "hello world"; + ssize_t send_size = s2n_send(send_conn, send_data, sizeof(send_data), &blocked); + RESULT_GUARD_POSIX(send_size); + RESULT_ENSURE_EQ(send_size, sizeof(send_data)); + + uint8_t recv_data[sizeof(send_data)] = { 0 }; + ssize_t recv_size = s2n_recv(recv_conn, recv_data, send_size, &blocked); + RESULT_GUARD_POSIX(recv_size); + RESULT_ENSURE_EQ(recv_size, send_size); + RESULT_ENSURE_EQ(memcmp(recv_data, send_data, send_size), 0); + + return S2N_RESULT_OK; +} diff --git a/tests/testlib/s2n_testlib.h b/tests/testlib/s2n_testlib.h index 113cd2e1b5b..160a439a2fe 100644 --- a/tests/testlib/s2n_testlib.h +++ b/tests/testlib/s2n_testlib.h @@ -233,6 +233,7 @@ S2N_RESULT s2n_negotiate_test_server_and_client_until_message(struct s2n_connect int s2n_shutdown_test_server_and_client(struct s2n_connection *server_conn, struct s2n_connection *client_conn); S2N_RESULT s2n_negotiate_test_server_and_client_with_early_data(struct s2n_connection *server_conn, struct s2n_connection *client_conn, struct s2n_blob *early_data_to_send, struct s2n_blob *early_data_received); +S2N_RESULT s2n_send_and_recv_test(struct s2n_connection *send_conn, struct s2n_connection *recv_conn); /* Testing only with easily constructed contiguous data buffers could hide errors. * We should use iovecs where every buffer is allocated separately. @@ -291,3 +292,5 @@ extern const s2n_parsed_extension EMPTY_PARSED_EXTENSIONS[S2N_PARSED_EXTENSIONS_ int s2n_kem_recv_public_key_fuzz_test(const uint8_t *buf, size_t len, struct s2n_kem_params *kem_params); int s2n_kem_recv_ciphertext_fuzz_test(const uint8_t *buf, size_t len, struct s2n_kem_params *kem_params); int s2n_kem_recv_ciphertext_fuzz_test_init(const char *kat_file_path, struct s2n_kem_params *kem_params); + +S2N_RESULT s2n_resumption_test_ticket_key_setup(struct s2n_config *config); diff --git a/tests/unit/s2n_auth_selection_test.c b/tests/unit/s2n_auth_selection_test.c index 79c5772ca15..9f2691f5118 100644 --- a/tests/unit/s2n_auth_selection_test.c +++ b/tests/unit/s2n_auth_selection_test.c @@ -33,8 +33,8 @@ #define RSA_PKCS1_SIG_SCHEME &s2n_rsa_pkcs1_md5_sha1 #define RSA_PSS_PSS_SIG_SCHEME &s2n_rsa_pss_pss_sha256 #define RSA_PSS_RSAE_SIG_SCHEME &s2n_rsa_pss_rsae_sha256 -#define ECDSA_SIG_SCHEME &s2n_ecdsa_secp384r1_sha384 -#define ECDSA_SIG_SCHEME_OTHER_CURVE &s2n_ecdsa_secp256r1_sha256 +#define ECDSA_SIG_SCHEME &s2n_ecdsa_sha384 +#define ECDSA_SIG_SCHEME_OTHER_CURVE &s2n_ecdsa_sha256 #define EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(x) \ if (s2n_is_rsa_pss_certs_supported()) { \ @@ -102,31 +102,31 @@ int main(int argc, char **argv) /* Test: not valid if certs not available */ { /* No certs exist */ - s2n_connection_set_config(conn, no_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, no_certs_config)); EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); /* RSA certs exist */ - s2n_connection_set_config(conn, rsa_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, rsa_cert_config)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); /* RSA-PSS certs exist */ - s2n_connection_set_config(conn, rsa_pss_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, rsa_pss_cert_config)); EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); /* ECDSA certs exist */ - s2n_connection_set_config(conn, ecdsa_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, ecdsa_cert_config)); EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); /* All certs exist */ - s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, all_certs_config)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); @@ -144,43 +144,47 @@ int main(int argc, char **argv) conn->secure->cipher_suite = NO_AUTH_CIPHER_SUITE; /* No certs exist */ - s2n_connection_set_config(conn, no_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, no_certs_config)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); /* RSA certs exist */ - s2n_connection_set_config(conn, rsa_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, rsa_cert_config)); EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); /* RSA-PSS certs exist */ - s2n_connection_set_config(conn, rsa_pss_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, rsa_pss_cert_config)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); /* ECDSA certs exist */ - s2n_connection_set_config(conn, ecdsa_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, ecdsa_cert_config)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); /* All certs exist */ - s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, all_certs_config)); EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); } - /* Test: If signature algorithm specifies curve, must match cert curve */ + /* Test: If signature algorithm is TLS1.3 ECDSA, must match cert curve */ { + DEFER_CLEANUP(struct s2n_connection *test_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + test_conn->actual_protocol_version = S2N_TLS13; + struct s2n_cert_chain_and_key *ecdsa_cert_chain_for_other_curve = NULL; EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain_for_other_curve, S2N_ECDSA_P256_PKCS1_CERT_CHAIN, S2N_ECDSA_P256_PKCS1_KEY)); @@ -189,15 +193,15 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( ecdsa_cert_config_for_other_curve, ecdsa_cert_chain_for_other_curve)); - conn->secure->cipher_suite = NO_AUTH_CIPHER_SUITE; + test_conn->secure->cipher_suite = NO_AUTH_CIPHER_SUITE; - s2n_connection_set_config(conn, ecdsa_cert_config); - EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); - EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME_OTHER_CURVE)); + EXPECT_SUCCESS(s2n_connection_set_config(test_conn, ecdsa_cert_config)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(test_conn, ECDSA_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(test_conn, ECDSA_SIG_SCHEME_OTHER_CURVE)); - s2n_connection_set_config(conn, ecdsa_cert_config_for_other_curve); - EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); - EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME_OTHER_CURVE)); + EXPECT_SUCCESS(s2n_connection_set_config(test_conn, ecdsa_cert_config_for_other_curve)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(test_conn, ECDSA_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(test_conn, ECDSA_SIG_SCHEME_OTHER_CURVE)); EXPECT_SUCCESS(s2n_config_free(ecdsa_cert_config_for_other_curve)); EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain_for_other_curve)); @@ -205,7 +209,7 @@ int main(int argc, char **argv) /* Test: If cipher suite specifies auth type, auth type must be valid for sig alg on server */ { - s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, all_certs_config)); /* RSA auth type */ conn->secure->cipher_suite = RSA_AUTH_CIPHER_SUITE; @@ -243,7 +247,7 @@ int main(int argc, char **argv) /* Test: RSA-PSS requires a non-ephemeral kex */ { - s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, all_certs_config)); /* ephemeral key */ conn->secure->cipher_suite = &s2n_dhe_rsa_with_3des_ede_cbc_sha; /* kex = (dhe) */ @@ -301,7 +305,7 @@ int main(int argc, char **argv) struct s2n_cert_chain_and_key *chosen_certs = NULL; /* Requested cert chain exists */ - s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, all_certs_config)); struct s2n_signature_scheme test_sig_scheme = { 0 }; conn->handshake_params.server_cert_sig_scheme = &test_sig_scheme; @@ -326,7 +330,7 @@ int main(int argc, char **argv) EXPECT_EQUAL(chosen_certs, ecdsa_cert_chain); /* Requested cert chain does NOT exist */ - s2n_connection_set_config(conn, no_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, no_certs_config)); /* cppcheck-suppress redundantAssignment */ test_sig_scheme.sig_alg = S2N_SIGNATURE_RSA; @@ -354,10 +358,10 @@ int main(int argc, char **argv) /* Test all possible combos */ { struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); - s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, all_certs_config)); /* No certs exist */ - s2n_connection_set_config(conn, no_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, no_certs_config)); EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); @@ -375,7 +379,7 @@ int main(int argc, char **argv) EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); /* RSA certs exist */ - s2n_connection_set_config(conn, rsa_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, rsa_cert_config)); EXPECT_SUCCESS(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, rsa_cert_chain)); EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); @@ -393,7 +397,7 @@ int main(int argc, char **argv) EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); /* RSA_PSS certs exist */ - s2n_connection_set_config(conn, rsa_pss_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, rsa_pss_cert_config)); EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, rsa_pss_cert_chain)); @@ -411,7 +415,7 @@ int main(int argc, char **argv) EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); /* ECDSA certs exist */ - s2n_connection_set_config(conn, ecdsa_cert_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, ecdsa_cert_config)); EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); @@ -429,7 +433,7 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, ecdsa_cert_chain)); /* All certs exist */ - s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, all_certs_config)); EXPECT_SUCCESS(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, rsa_cert_chain)); EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, rsa_pss_cert_chain)); diff --git a/tests/unit/s2n_cert_authorities_test.c b/tests/unit/s2n_cert_authorities_test.c new file mode 100644 index 00000000000..a2a553c44fb --- /dev/null +++ b/tests/unit/s2n_cert_authorities_test.c @@ -0,0 +1,215 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/extensions/s2n_cert_authorities.h" + +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_bitmap.h" + +int s2n_parse_client_hello(struct s2n_connection *conn); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *cert_chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + s2n_extension_type_id temp_id = s2n_unsupported_extension; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id( + s2n_cert_authorities_extension.iana_value, &temp_id)); + const s2n_extension_type_id ca_ext_id = temp_id; + + /* Test: s2n_certificate_authorities_extension.send */ + { + /* Test: writes whatever CA data is available */ + { + const uint8_t ca_data[] = "these are my CAs"; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, sizeof(ca_data))); + EXPECT_MEMCPY_SUCCESS(config->cert_authorities.data, ca_data, sizeof(ca_data)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_SUCCESS(s2n_cert_authorities_extension.send(conn, &output)); + + uint16_t size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&output, &size)); + EXPECT_EQUAL(size, sizeof(ca_data)); + EXPECT_EQUAL(size, s2n_stuffer_data_available(&output)); + + uint8_t *data = s2n_stuffer_raw_read(&output, size); + EXPECT_NOT_NULL(data); + EXPECT_BYTEARRAY_EQUAL(data, ca_data, sizeof(ca_data)); + }; + }; + + /* Test: s2n_certificate_authorities_extension.should_send */ + { + /* Test: do not send for TLS1.2 */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, 10)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_extension_send(&s2n_cert_authorities_extension, + conn, &output)); + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_extension_send(&s2n_cert_authorities_extension, + conn, &output)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&output), 0); + }; + + /* Test: do not send if no CA data set */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(s2n_cert_authorities_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, 10)); + EXPECT_TRUE(s2n_cert_authorities_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_free(&config->cert_authorities)); + EXPECT_FALSE(s2n_cert_authorities_extension.should_send(conn)); + }; + }; + + /* Test: ClientHello extension ignored + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.4 + *= type=test + *# The client MAY send the "certificate_authorities" extension in the + *# ClientHello message. + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, 10)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_parse_client_hello(client)); + struct s2n_client_hello *client_hello = &client->client_hello; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + struct s2n_stuffer *input = &server->handshake.io; + + /* Copy ClientHello, except extensions */ + size_t size_without_extensions = client_hello->raw_message.size + - client_hello->extensions.raw.size + - sizeof(uint16_t) /* Extensions size */; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(input, + client_hello->raw_message.data, size_without_extensions)); + + /* Save space for new extensions size */ + struct s2n_stuffer_reservation extensions_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(input, &extensions_size)); + + /* Write the certificate_authorities extension. + * The client isn't allowed to write it, so use the server. + */ + server->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_extension_send(&s2n_cert_authorities_extension, + server, input)); + + /* Write the rest of the extensions */ + EXPECT_SUCCESS(s2n_stuffer_write(input, &client_hello->extensions.raw)); + + /* Update the extensions size */ + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extensions_size)); + + /* Server should be able to successfully receive the modified ClientHello */ + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + EXPECT_TRUE(server->client_hello.parsed); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + s2n_parsed_extension *extension = &server->client_hello.extensions.parsed_extensions[ca_ext_id]; + EXPECT_TRUE(extension->extension.size > 0); + EXPECT_FALSE(extension->processed); + EXPECT_EQUAL(extension->extension_type, s2n_cert_authorities_extension.iana_value); + }; + + /* Self-talk test: CertificateRequest extension parsed + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.4 + *= type=test + *# The server MAY send it in the CertificateRequest message. + **/ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, 10)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server)); + + /* Server sent extension */ + EXPECT_TRUE(S2N_CBIT_TEST(server->extension_requests_sent, ca_ext_id)); + EXPECT_FALSE(S2N_CBIT_TEST(server->extension_requests_received, ca_ext_id)); + + /* Client received extension */ + EXPECT_FALSE(S2N_CBIT_TEST(client->extension_requests_sent, ca_ext_id)); + EXPECT_TRUE(S2N_CBIT_TEST(client->extension_requests_received, ca_ext_id)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_auth_handshake_test.c b/tests/unit/s2n_client_auth_handshake_test.c index 3b29c02604c..168242bf451 100644 --- a/tests/unit/s2n_client_auth_handshake_test.c +++ b/tests/unit/s2n_client_auth_handshake_test.c @@ -47,8 +47,8 @@ int s2n_test_client_auth_negotiation(struct s2n_config *server_config, struct s2 client_conn->server_protocol_version = S2N_TLS13; client_conn->client_protocol_version = S2N_TLS13; client_conn->actual_protocol_version = S2N_TLS13; - client_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_secp256r1_sha256; - client_conn->handshake_params.client_cert_sig_scheme = &s2n_ecdsa_secp256r1_sha256; + client_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; + client_conn->handshake_params.client_cert_sig_scheme = &s2n_ecdsa_sha256; client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; if (!no_cert) { client_conn->handshake_params.our_chain_and_key = ecdsa_cert; @@ -58,7 +58,7 @@ int s2n_test_client_auth_negotiation(struct s2n_config *server_config, struct s2 server_conn->server_protocol_version = S2N_TLS13; server_conn->client_protocol_version = S2N_TLS13; server_conn->actual_protocol_version = S2N_TLS13; - server_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_secp256r1_sha256; + server_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_sha256; server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; if (no_cert) { @@ -390,6 +390,33 @@ int main(int argc, char **argv) S2N_ERR_UNEXPECTED_CERT_REQUEST); }; + /* Test missing certificate request */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* Require client auth on the client, but not on the server */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, S2N_CERT_AUTH_NONE)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_MISSING_CERT_REQUEST); + }; + /* By default, client accepts certificate requests */ { DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); @@ -416,5 +443,90 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); }; + /* Test: all combinations of client and server mutual auth settings produce useful errors + * + * Customers have struggled to correctly configure client auth in the past. + * We should provide very specific and helpful errors to facilitate debugging. + * Do not allow any generic errors like S2N_ERR_BAD_MESSAGE. + */ + { + const int S2N_CERT_AUTH_DEFAULT = -255; + int all_options[] = { + S2N_CERT_AUTH_DEFAULT, + S2N_CERT_AUTH_NONE, + S2N_CERT_AUTH_OPTIONAL, + S2N_CERT_AUTH_REQUIRED + }; + + struct { + int client_auth_type; + int server_auth_type; + bool client_cert_exists; + uint8_t version; + } test_cases[100] = { 0 }; + size_t test_case_count = 0; + + for (size_t client_i = 0; client_i < s2n_array_len(all_options); client_i++) { + for (size_t server_i = 0; server_i < s2n_array_len(all_options); server_i++) { + for (size_t cert_i = 0; cert_i <= 1; cert_i++) { + for (size_t version = S2N_TLS12; version <= S2N_TLS13; version++) { + EXPECT_TRUE(test_case_count < s2n_array_len(test_cases)); + test_cases[test_case_count].client_auth_type = all_options[client_i]; + test_cases[test_case_count].server_auth_type = all_options[server_i]; + test_cases[test_case_count].client_cert_exists = (cert_i == 1); + test_cases[test_case_count].version = version; + test_case_count++; + } + } + } + } + + for (size_t i = 0; i < test_case_count; i++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + if (test_cases[i].client_cert_exists) { + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + } + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + server->server_protocol_version = test_cases[i].version; + + if (test_cases[i].client_auth_type != S2N_CERT_AUTH_DEFAULT) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client, test_cases[i].client_auth_type)); + } + + if (test_cases[i].server_auth_type != S2N_CERT_AUTH_DEFAULT) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server, test_cases[i].server_auth_type)); + } + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + int result = s2n_negotiate_test_server_and_client(server, client); + EXPECT_EQUAL(client->actual_protocol_version, test_cases[i].version); + EXPECT_EQUAL(server->actual_protocol_version, test_cases[i].version); + if (result != S2N_SUCCESS) { + EXPECT_TRUE(s2n_errno == S2N_ERR_MISSING_CERT_REQUEST + || s2n_errno == S2N_ERR_UNEXPECTED_CERT_REQUEST + || s2n_errno == S2N_ERR_MISSING_CLIENT_CERT); + } + } + }; + END_TEST(); } diff --git a/tests/unit/s2n_client_hello_request_test.c b/tests/unit/s2n_client_hello_request_test.c index f1d3743b6f0..ee28287676d 100644 --- a/tests/unit/s2n_client_hello_request_test.c +++ b/tests/unit/s2n_client_hello_request_test.c @@ -24,27 +24,6 @@ static const uint8_t hello_request_msg[] = { /* empty message body */ }; -static S2N_RESULT s2n_test_send_and_recv(struct s2n_connection *send_conn, struct s2n_connection *recv_conn) -{ - RESULT_ENSURE_REF(send_conn); - RESULT_ENSURE_REF(recv_conn); - - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - - const uint8_t send_data[] = "hello world"; - ssize_t send_size = s2n_send(send_conn, send_data, sizeof(send_data), &blocked); - RESULT_GUARD_POSIX(send_size); - RESULT_ENSURE_EQ(send_size, sizeof(send_data)); - - uint8_t recv_data[sizeof(send_data)] = { 0 }; - ssize_t recv_size = s2n_recv(recv_conn, recv_data, send_size, &blocked); - RESULT_GUARD_POSIX(recv_size); - RESULT_ENSURE_EQ(recv_size, send_size); - EXPECT_BYTEARRAY_EQUAL(recv_data, send_data, send_size); - - return S2N_RESULT_OK; -} - static S2N_RESULT s2n_send_client_hello_request(struct s2n_connection *server_conn) { RESULT_ENSURE_REF(server_conn); @@ -213,16 +192,16 @@ int main(int argc, char **argv) EXPECT_TRUE(client_conn->secure_renegotiation); /* Send some data */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); /* Send the hello request message. */ EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* Send some more data */ for (size_t i = 0; i < 10; i++) { - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); EXPECT_TRUE(s2n_connection_check_io_status(client_conn, S2N_IO_FULL_DUPLEX)); } }; @@ -261,8 +240,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert NOT sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); /* Callback was not set */ EXPECT_NULL(client_conn->config->renegotiate_request_cb); @@ -309,8 +288,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(client_conn, server_conn), S2N_ERR_ALERT); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(client_conn, server_conn), S2N_ERR_ALERT); EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_NO_RENEGOTIATION); /* Callback triggered */ @@ -353,8 +332,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert NOT sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); /* Callback triggered */ EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); @@ -391,8 +370,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert NOT sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); /* Callback triggered */ EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); @@ -445,8 +424,8 @@ int main(int argc, char **argv) EXPECT_OK(s2n_send_client_hello_request(server_conn)); /* no_renegotation alert sent and received */ - EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); - EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(client_conn, server_conn), S2N_ERR_ALERT); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(client_conn, server_conn), S2N_ERR_ALERT); EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_NO_RENEGOTIATION); /* Callback was not triggered */ @@ -491,7 +470,7 @@ int main(int argc, char **argv) * Applications won't be able to set s2n_errno to a meaningful value, * so we need to set it to S2N_ERR_CANCELED for them. */ - EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(server_conn, client_conn), S2N_ERR_CANCELLED); + EXPECT_ERROR_WITH_ERRNO(s2n_send_and_recv_test(server_conn, client_conn), S2N_ERR_CANCELLED); }; /* Test: SSLv3 sends a fatal handshake_failure alert instead of no_renegotiate diff --git a/tests/unit/s2n_client_psk_extension_test.c b/tests/unit/s2n_client_psk_extension_test.c index 5f89b493b4a..b4106154df2 100644 --- a/tests/unit/s2n_client_psk_extension_test.c +++ b/tests/unit/s2n_client_psk_extension_test.c @@ -74,28 +74,6 @@ static int mock_time(void *data, uint64_t *nanoseconds) return S2N_SUCCESS; } -static int s2n_setup_ticket_key(struct s2n_config *config) -{ - /** - *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 - *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 - *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) - **/ - S2N_BLOB_FROM_HEX(ticket_key, - "077709362c2e32df0ddc3f0dc47bba63" - "90b6c73bb50f9c3122ec844ad7c2b3e5"); - - /* Set up encryption key */ - uint64_t current_time = 0; - uint8_t ticket_key_name[16] = "2016.07.26.15\0"; - - POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, 1)); - POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - POSIX_GUARD(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - return S2N_SUCCESS; -} - static S2N_RESULT s2n_setup_encrypted_ticket(struct s2n_connection *conn, struct s2n_stuffer *output) { conn->tls13_ticket_fields = (struct s2n_ticket_fields){ 0 }; @@ -701,7 +679,7 @@ int main(int argc, char **argv) struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -736,7 +714,7 @@ int main(int argc, char **argv) struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -809,7 +787,7 @@ int main(int argc, char **argv) struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -1044,11 +1022,11 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(config_with_cb); uint16_t expected_wire_choice = 0; EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config_with_cb, s2n_test_select_psk_identity_callback, &expected_wire_choice)); - EXPECT_SUCCESS(s2n_setup_ticket_key(config_with_cb)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config_with_cb)); struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); @@ -1549,7 +1527,7 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); - EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); diff --git a/tests/unit/s2n_connection_serialize_test.c b/tests/unit/s2n_connection_serialize_test.c new file mode 100644 index 00000000000..b40a15d92b3 --- /dev/null +++ b/tests/unit/s2n_connection_serialize_test.c @@ -0,0 +1,871 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_connection_serialize.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" + +#define S2N_SERIALIZED_CONN_TLS13_SHA256_SIZE 126 + +#define TEST_SEQUENCE_NUM 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +#define TEST_TLS13_SECRET 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +static S2N_RESULT s2n_test_key_update(struct s2n_connection *client_conn, struct s2n_connection *server_conn) +{ + /* One side initiates key update */ + RESULT_GUARD_POSIX(s2n_connection_request_key_update(client_conn, S2N_KEY_UPDATE_NOT_REQUESTED)); + + /* Sending and receiving is successful after key update */ + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + EXPECT_EQUAL(client_conn->send_key_updated, 1); + EXPECT_EQUAL(server_conn->recv_key_updated, 1); + + /* Other side initiates key update */ + EXPECT_SUCCESS(s2n_connection_request_key_update(server_conn, S2N_KEY_UPDATE_NOT_REQUESTED)); + + /* Sending and receiving is successful after key update */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_EQUAL(client_conn->recv_key_updated, 1); + EXPECT_EQUAL(server_conn->send_key_updated, 1); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_ticket_pickup(struct s2n_connection *conn, struct s2n_blob *session_ticket) +{ + int session_length = s2n_connection_get_session_length(conn); + EXPECT_NOT_EQUAL(session_length, 0); + EXPECT_SUCCESS(s2n_realloc(session_ticket, session_length)); + EXPECT_SUCCESS(s2n_connection_get_session(conn, session_ticket->data, session_length)); + session_ticket->size = session_length; + + return S2N_RESULT_OK; +} + +static int s2n_test_reneg_cb(struct s2n_connection *conn, void *context, + s2n_renegotiate_response *response) +{ + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *tls12_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(tls12_config)); + EXPECT_SUCCESS(s2n_config_set_serialization_version(tls12_config, S2N_SERIALIZED_CONN_V1)); + + DEFER_CLEANUP(struct s2n_config *tls13_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(tls13_config)); + EXPECT_SUCCESS(s2n_config_set_serialization_version(tls13_config, S2N_SERIALIZED_CONN_V1)); + /* Security policy that can negotiate TLS13 and has aes_128_gcm_sha256 as its preferred cipher suite */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_config, "20210825")); + + /* s2n_connection_serialization_length */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + uint32_t length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialization_length(conn, NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialization_length(NULL, &length), S2N_ERR_NULL); + }; + + /* Length is correct for all possible cipher suites in TLS1.3 */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, tls13_config)); + conn->actual_protocol_version = S2N_TLS13; + + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; + conn->secure->cipher_suite = cipher_suite; + uint8_t expected_secret_size = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(cipher_suite->prf_alg, &expected_secret_size)); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(conn, &length)); + EXPECT_EQUAL(length, S2N_SERIALIZED_CONN_FIXED_SIZE + (expected_secret_size * 3)); + } + }; + + /* Length is correct for TLS1.2 */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, tls12_config)); + conn->actual_protocol_version = S2N_TLS12; + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(conn, &length)); + EXPECT_EQUAL(length, S2N_SERIALIZED_CONN_TLS12_SIZE); + }; + }; + + /* s2n_connection_serialize */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + uint8_t data = 0; + uint32_t length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(NULL, &data, length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(conn, NULL, length), S2N_ERR_NULL); + }; + + /* Invalid usage checks */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint8_t buffer[10] = { 0 }; + uint32_t length = sizeof(buffer); + + /* Format version must be set before calling this function */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(conn, buffer, length), + S2N_ERR_INVALID_STATE); + + /* Negotiation must be complete before calling this function */ + EXPECT_SUCCESS(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(conn, buffer, length), + S2N_ERR_HANDSHAKE_NOT_COMPLETE); + + /* Buffer must be large enough to hold entire serialized length */ + EXPECT_OK(s2n_skip_handshake(conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(conn, buffer, length), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + }; + + /* Serializes TLS 1.2 */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS12); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, buffer, sizeof(buffer))); + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init_written(&stuffer, &blob)); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); + EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); + + uint64_t serialized_version = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&stuffer, &serialized_version)); + EXPECT_EQUAL(serialized_version, S2N_SERIALIZED_CONN_V1); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, protocol_version, + S2N_TLS_PROTOCOL_VERSION_LEN)); + EXPECT_EQUAL((protocol_version[0] * 10) + protocol_version[1], S2N_TLS12); + + uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + EXPECT_BYTEARRAY_EQUAL(cipher_suite, server_conn->secure->cipher_suite->iana_value, + S2N_TLS_CIPHER_SUITE_LEN); + + uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN)); + uint8_t expected_sequence_number[] = { 0, 0, 0, 0, 0, 0, 0, 1 }; + EXPECT_BYTEARRAY_EQUAL(client_sequence_number, expected_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + uint8_t server_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, server_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_BYTEARRAY_EQUAL(server_sequence_number, expected_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + uint16_t frag_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &frag_len)); + EXPECT_EQUAL(frag_len, S2N_DEFAULT_FRAGMENT_LENGTH); + + uint8_t master_secret[S2N_TLS_SECRET_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, master_secret, S2N_TLS_SECRET_LEN)); + EXPECT_BYTEARRAY_EQUAL(master_secret, server_conn->secrets.version.tls12.master_secret, + S2N_TLS_SECRET_LEN); + + uint8_t client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_random, S2N_TLS_RANDOM_DATA_LEN)); + EXPECT_BYTEARRAY_EQUAL(client_random, server_conn->handshake_params.client_random, + S2N_TLS_RANDOM_DATA_LEN); + + uint8_t server_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, server_random, S2N_TLS_RANDOM_DATA_LEN)); + EXPECT_BYTEARRAY_EQUAL(server_random, server_conn->handshake_params.server_random, + S2N_TLS_RANDOM_DATA_LEN); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + }; + + /* Serializes TLS 1.3 */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS13); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS13_SHA256_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, buffer, sizeof(buffer))); + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init_written(&stuffer, &blob)); + + uint32_t length = 0; + EXPECT_SUCCESS(s2n_connection_serialization_length(server_conn, &length)); + EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); + + uint64_t serialized_version = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&stuffer, &serialized_version)); + EXPECT_EQUAL(serialized_version, S2N_SERIALIZED_CONN_V1); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, protocol_version, + S2N_TLS_PROTOCOL_VERSION_LEN)); + EXPECT_EQUAL((protocol_version[0] * 10) + protocol_version[1], S2N_TLS13); + + uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + EXPECT_BYTEARRAY_EQUAL(cipher_suite, server_conn->secure->cipher_suite->iana_value, + S2N_TLS_CIPHER_SUITE_LEN); + + uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN)); + uint8_t expected_sequence_number[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + EXPECT_BYTEARRAY_EQUAL(client_sequence_number, expected_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + uint8_t server_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, server_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_BYTEARRAY_EQUAL(server_sequence_number, expected_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + uint16_t frag_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &frag_len)); + EXPECT_EQUAL(frag_len, S2N_DEFAULT_FRAGMENT_LENGTH); + + uint8_t client_secret[SHA256_DIGEST_LENGTH] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, client_secret, SHA256_DIGEST_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(client_secret, server_conn->secrets.version.tls13.client_app_secret, + SHA256_DIGEST_LENGTH); + + uint8_t server_secret[SHA256_DIGEST_LENGTH] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, server_secret, SHA256_DIGEST_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(server_secret, server_conn->secrets.version.tls13.server_app_secret, + SHA256_DIGEST_LENGTH); + + uint8_t resumption_secret[SHA256_DIGEST_LENGTH] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, resumption_secret, SHA256_DIGEST_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(resumption_secret, server_conn->secrets.version.tls13.resumption_master_secret, + SHA256_DIGEST_LENGTH); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + }; + + /* IO buffers must be empty before calling this function */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + uint8_t data[] = "Hello"; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_send(client_conn, data, sizeof(data), &blocked)); + + /* Partial read so that some data remains in the buffer */ + uint8_t recv_buf[10] = { 0 }; + EXPECT_SUCCESS(s2n_recv(server_conn, &recv_buf, 1, &blocked)); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_serialize(server_conn, buffer, + sizeof(buffer)), + S2N_ERR_INVALID_STATE); + + /* Finish reading to successfully get the serialized connection */ + EXPECT_SUCCESS(s2n_recv(server_conn, &recv_buf, sizeof(recv_buf), &blocked)); + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + }; + }; + + /* s2n_connection_deserialize */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + uint8_t buffer = 0; + uint32_t length = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_deserialize(NULL, &buffer, length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_deserialize(conn, NULL, length), S2N_ERR_NULL); + }; + + /* Errors if format version is unknown */ + { + uint8_t test_context[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, /* Unknown serialized_connection version */ + 0x03, 0x04, /* TLS 1.3 */ + TLS_AES_128_GCM_SHA256, + TEST_SEQUENCE_NUM, /* Client sequence num */ + TEST_SEQUENCE_NUM, /* Server sequence num */ + 0x01, 0x01, /* Test Fragment length */ + TEST_TLS13_SECRET, /* Client app secret */ + TEST_TLS13_SECRET, /* Server app secret */ + TEST_TLS13_SECRET /* Resumption master secret */ + }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_deserialize(client_conn, test_context, + sizeof(test_context)), + S2N_ERR_INVALID_SERIALIZED_CONNECTION); + }; + + /* Succeeds if format version is known */ + { + uint8_t test_context[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, S2N_SERIALIZED_CONN_V1, + 0x03, 0x04, /* TLS 1.3 */ + TLS_AES_128_GCM_SHA256, + TEST_SEQUENCE_NUM, /* Client sequence num */ + TEST_SEQUENCE_NUM, /* Server sequence num */ + 0x01, 0x01, /* Test Fragment length */ + TEST_TLS13_SECRET, /* Client app secret */ + TEST_TLS13_SECRET, /* Server app secret */ + TEST_TLS13_SECRET /* Resumption master secret */ + }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(client_conn, test_context, + sizeof(test_context))); + }; + }; + + struct s2n_config *config_array[] = { tls12_config, tls13_config }; + + /* Self-talk: Client can be serialized and deserialized and continue sending and receiving data + * in TLS1.2 and TLS1.3 */ + for (size_t i = 0; i < s2n_array_len(config_array); i++) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_array[i])); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_array[i])); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Preliminary send and receive */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(client_conn, buffer, sizeof(buffer))); + + /* Initialize new client connection and deserialize the connection */ + DEFER_CLEANUP(struct s2n_connection *new_client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(new_client_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(new_client_conn, buffer, sizeof(buffer))); + + /* Wipe and re-initialize IO pipes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.client_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(new_client_conn, server_conn, &io_pair)); + + /* Client can send and recv as usual */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, new_client_conn)); + EXPECT_OK(s2n_send_and_recv_test(new_client_conn, server_conn)); + }; + + /* Self-talk: Server can be serialized and deserialized and continue sending and receiving data + * in TLS1.2 and TLS1.3 */ + for (size_t i = 0; i < s2n_array_len(config_array); i++) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_array[i])); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_array[i])); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Preliminary send and receive */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + + uint8_t buffer[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, buffer, sizeof(buffer))); + + /* Initialize new server connection and deserialize the connection */ + DEFER_CLEANUP(struct s2n_connection *new_server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(new_server_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(new_server_conn, buffer, sizeof(buffer))); + + /* Wipe and re-initialize IO pipes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.client_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, new_server_conn, &io_pair)); + + /* Server can send and recv as usual */ + EXPECT_OK(s2n_send_and_recv_test(new_server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, new_server_conn)); + }; + + /* Self-talk: Test interaction between TLS1.2 session resumption and serialization */ + { + DEFER_CLEANUP(struct s2n_config *resumption_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(resumption_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(resumption_config)); + EXPECT_SUCCESS(s2n_config_set_serialization_version(resumption_config, S2N_SERIALIZED_CONN_V1)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(resumption_config)); + + uint8_t serialized_conn[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + + /* Initial handshake */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, resumption_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, resumption_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS12); + + /* Session ticket should have been received in the handshake */ + EXPECT_TRUE(s2n_connection_get_session_length(client_conn) > 0); + + EXPECT_SUCCESS(s2n_connection_serialize(client_conn, serialized_conn, sizeof(serialized_conn))); + }; + + /* Deserialized connection has no ticket to retrieve */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(client_conn, serialized_conn, sizeof(serialized_conn))); + + /* Once deserialized the session ticket is no longer available. */ + EXPECT_TRUE(s2n_connection_get_session_length(client_conn) == 0); + }; + }; + + /* Self-talk: Test interaction between TLS1.3 session resumption and serialization. */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *resumption_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(resumption_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(resumption_config)); + EXPECT_SUCCESS(s2n_config_set_serialization_version(resumption_config, S2N_SERIALIZED_CONN_V1)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(resumption_config, "default_tls13")); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(resumption_config)); + + /* Client is serialized. Can read a session ticket before/after deserialization and resume with it. */ + { + uint8_t serialized_conn[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + DEFER_CLEANUP(struct s2n_blob session_ticket_0 = { 0 }, s2n_free); + DEFER_CLEANUP(struct s2n_blob session_ticket_1 = { 0 }, s2n_free); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, resumption_config)); + + /* Initial handshake */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, resumption_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS13); + + /* Client does a read here. Picks up initial ticket */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_ticket_pickup(client_conn, &session_ticket_0)); + + EXPECT_SUCCESS(s2n_connection_serialize(client_conn, serialized_conn, sizeof(serialized_conn))); + }; + + /* Pick up ticket after deserialization */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(client_conn, serialized_conn, sizeof(serialized_conn))); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, resumption_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_add_new_tickets_to_send(server_conn, 1)); + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + + EXPECT_OK(s2n_ticket_pickup(client_conn, &session_ticket_1)); + }; + + /* Resumption handshake with initial ticket */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, resumption_config)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, session_ticket_0.data, session_ticket_0.size)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + }; + + /* Successful resumption handshake with second ticket */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, resumption_config)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, session_ticket_1.data, session_ticket_1.size)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + }; + }; + + /* Server is serialized. Can write a session ticket before/after deserialization and resumption can + * occur with it. */ + { + uint8_t serialized_conn[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + DEFER_CLEANUP(struct s2n_blob session_ticket_0 = { 0 }, s2n_free); + DEFER_CLEANUP(struct s2n_blob session_ticket_1 = { 0 }, s2n_free); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, resumption_config)); + + /* Initial handshake */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, resumption_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS13); + + /* Client picks up the first ticket */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_ticket_pickup(client_conn, &session_ticket_0)); + + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, serialized_conn, sizeof(serialized_conn))); + }; + + /* Send ticket after deserialization */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(server_conn, serialized_conn, sizeof(serialized_conn))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, resumption_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_ticket_pickup(client_conn, &session_ticket_1)); + }; + + /* Resumption handshake with initial ticket */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, resumption_config)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, session_ticket_0.data, session_ticket_0.size)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + }; + + /* Resumption handshake with second ticket */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, resumption_config)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, session_ticket_1.data, session_ticket_1.size)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + }; + }; + }; + + /* Self talk: Test interaction between key update and serialization */ + if (s2n_is_tls13_fully_supported()) { + /* Deserialized client can do a keyupdate */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + uint8_t serialized_conn[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + + /* Initial handshake */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS13); + + /* Preliminary send and receive */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + + EXPECT_SUCCESS(s2n_connection_serialize(client_conn, serialized_conn, sizeof(serialized_conn))); + }; + + /* Deserialize and keyupdate */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(client_conn, serialized_conn, sizeof(serialized_conn))); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_OK(s2n_test_key_update(client_conn, server_conn)); + }; + }; + + /* Deserialized server can do a keyupdate */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + + uint8_t serialized_conn[S2N_SERIALIZED_CONN_TLS12_SIZE] = { 0 }; + + /* Initial handshake */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server_conn), S2N_TLS13); + + /* Preliminary send and receive */ + EXPECT_OK(s2n_send_and_recv_test(server_conn, client_conn)); + EXPECT_OK(s2n_send_and_recv_test(client_conn, server_conn)); + + EXPECT_SUCCESS(s2n_connection_serialize(server_conn, serialized_conn, sizeof(serialized_conn))); + }; + + /* Deserialize and keyupdate */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_deserialize(server_conn, serialized_conn, sizeof(serialized_conn))); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + EXPECT_OK(s2n_test_key_update(client_conn, server_conn)); + }; + }; + }; + + /* Test: Renegotiation and serialization cannot both be set. + * + * Renegotiation is not available after serialization/deserialization. + * The information needed to perform renegotiation (i.e. client/server finished verify data + * and secure renegotiation flag) isn't stored during the serialization process and therefore + * isn't available post-deserialization. + * + * We could add that data to the serialized struct in the future, but for now, the user will get + * an error if they attempt to configure renegotiation after serialization, and vice versa. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + /* Setting a renegotiation callback means you cannot set a serialization version. */ + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_cb, NULL)); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1), + S2N_ERR_INVALID_STATE); + + /* Reset renegotiate cb */ + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, NULL)); + + /* Setting a serialized version means that you cannot set a renegotiation callback. */ + EXPECT_SUCCESS(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_cb, + NULL), + S2N_ERR_INVALID_STATE); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_connection_size_test.c b/tests/unit/s2n_connection_size_test.c index 9e53914b4cd..7847ad7de8b 100644 --- a/tests/unit/s2n_connection_size_test.c +++ b/tests/unit/s2n_connection_size_test.c @@ -45,7 +45,7 @@ int main(int argc, char **argv) } /* Carefully consider any increases to this number. */ - const uint16_t max_connection_size = 4290; + const uint16_t max_connection_size = 4350; const uint16_t min_connection_size = max_connection_size * 0.9; size_t connection_size = sizeof(struct s2n_connection); diff --git a/tests/unit/s2n_extended_master_secret_test.c b/tests/unit/s2n_extended_master_secret_test.c index c112114fa64..2541c203598 100644 --- a/tests/unit/s2n_extended_master_secret_test.c +++ b/tests/unit/s2n_extended_master_secret_test.c @@ -24,21 +24,8 @@ int main(int argc, char **argv) /* Test s2n_conn_set_handshake_type is processing EMS data correctly */ { struct s2n_config *config = NULL; - uint64_t current_time = 0; EXPECT_NOT_NULL(config = s2n_config_new()); - - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - uint8_t ticket_key_name[16] = "2016.07.26.15\0"; - /** - *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 - *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 - *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) - **/ - S2N_BLOB_FROM_HEX(ticket_key, - "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); /** *= https://tools.ietf.org/rfc/rfc7627#section-5.3 diff --git a/tests/unit/s2n_fips_rules_test.c b/tests/unit/s2n_fips_rules_test.c index 059f3f10db9..3d8fe131439 100644 --- a/tests/unit/s2n_fips_rules_test.c +++ b/tests/unit/s2n_fips_rules_test.c @@ -120,7 +120,6 @@ int main(int argc, char **argv) const struct s2n_signature_scheme *valid[] = { &s2n_ecdsa_sha256, &s2n_rsa_pkcs1_sha384, - &s2n_ecdsa_secp521r1_sha512, &s2n_rsa_pss_pss_sha256, }; for (size_t i = 0; i < s2n_array_len(valid); i++) { diff --git a/tests/unit/s2n_ktls_io_test.c b/tests/unit/s2n_ktls_io_test.c index eeaefad05f7..2f5aafcd922 100644 --- a/tests/unit/s2n_ktls_io_test.c +++ b/tests/unit/s2n_ktls_io_test.c @@ -1118,7 +1118,8 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type)); EXPECT_EQUAL(record_type, TLS_ALERT); - EXPECT_EQUAL(conn->in.blob.allocated, max_frag_len); + EXPECT_EQUAL(conn->buffer_in.blob.allocated, max_frag_len); + EXPECT_EQUAL(conn->in.blob.size, max_frag_len); EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), max_frag_len); uint8_t *read = s2n_stuffer_raw_read(&conn->in, max_frag_len); EXPECT_BYTEARRAY_EQUAL(read, test_data, max_frag_len); @@ -1152,7 +1153,8 @@ int main(int argc, char **argv) /* Verify that conn->in reflects the correct size of the "record" * read and doesn't just assume the maximum read size. */ - EXPECT_EQUAL(conn->in.blob.allocated, max_frag_len); + EXPECT_EQUAL(conn->buffer_in.blob.allocated, max_frag_len); + EXPECT_EQUAL(conn->in.blob.size, small_frag_len); EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), small_frag_len); uint8_t *read = s2n_stuffer_raw_read(&conn->in, small_frag_len); EXPECT_BYTEARRAY_EQUAL(read, test_data, small_frag_len); @@ -1172,6 +1174,8 @@ int main(int argc, char **argv) /* Write half the test data into conn->in */ const size_t offset = sizeof(test_data) / 2; EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, test_data, offset)); + /* Resize conn->buffer_in to match conn->in */ + EXPECT_SUCCESS(s2n_stuffer_resize(&conn->buffer_in, offset)); /* Write the other half into a new record */ size_t written = 0; @@ -1201,6 +1205,41 @@ int main(int argc, char **argv) read = s2n_stuffer_raw_read(&conn->in, offset_iovec.iov_len); EXPECT_BYTEARRAY_EQUAL(read, offset_iovec.iov_base, offset_iovec.iov_len); }; + + /* Test: Receive multiple records */ + { + const size_t small_frag_len = 10; + EXPECT_TRUE(small_frag_len < max_frag_len); + EXPECT_TRUE(small_frag_len < sizeof(test_data)); + struct iovec small_test_iovec = test_iovec; + small_test_iovec.iov_len = small_frag_len; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + for (size_t i = 0; i < 100; i++) { + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &small_test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, small_frag_len); + + uint8_t record_type = 0; + EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type)); + EXPECT_EQUAL(record_type, TLS_ALERT); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), written); + uint8_t *read = s2n_stuffer_raw_read(&conn->in, small_frag_len); + EXPECT_BYTEARRAY_EQUAL(read, test_data, small_frag_len); + + EXPECT_OK(s2n_record_wipe(conn)); + size_t space_remaining = s2n_stuffer_space_remaining(&conn->buffer_in); + EXPECT_EQUAL(space_remaining, max_frag_len); + } + }; }; /* Test: key encryption limit tracked */ diff --git a/tests/unit/s2n_ktls_test.c b/tests/unit/s2n_ktls_test.c index a57254c4432..37ae40c5b0a 100644 --- a/tests/unit/s2n_ktls_test.c +++ b/tests/unit/s2n_ktls_test.c @@ -387,6 +387,22 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn)); }; + /* Fail if buffer_in contains any data. + * A connection that will enable ktls needs to disable recv_greedy + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&server_conn->buffer_in, 1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), + S2N_ERR_KTLS_UNSUPPORTED_CONN); + + EXPECT_SUCCESS(s2n_stuffer_skip_read(&server_conn->buffer_in, 1)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn)); + }; + /* Fail if not using managed IO for send */ { DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), @@ -436,6 +452,23 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server)); } + /* Fail if serialization is a possibility */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(client)); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + EXPECT_SUCCESS(s2n_config_set_serialization_version(config, S2N_SERIALIZED_CONN_V1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(client), S2N_ERR_KTLS_UNSUPPORTED_CONN); + + /* Removing the intent to serialize means that ktls enable now succeeds */ + config->serialized_connection_version = S2N_SERIALIZED_CONN_NONE; + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(client)); + } + /* Call setsockopt correctly to configure tls crypto */ { struct s2n_cipher_suite test_cipher_suite = s2n_rsa_with_aes_256_gcm_sha384; diff --git a/tests/unit/s2n_psk_offered_test.c b/tests/unit/s2n_psk_offered_test.c index de01cd25b1e..8e039b7c61c 100644 --- a/tests/unit/s2n_psk_offered_test.c +++ b/tests/unit/s2n_psk_offered_test.c @@ -22,28 +22,6 @@ #define MILLIS_TO_NANOS(millis) (millis * (uint64_t) ONE_MILLISEC_IN_NANOS) -static int s2n_setup_ticket_key(struct s2n_config *config) -{ - /** - *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 - *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 - *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) - **/ - S2N_BLOB_FROM_HEX(ticket_key, - "077709362c2e32df0ddc3f0dc47bba63" - "90b6c73bb50f9c3122ec844ad7c2b3e5"); - - /* Set up encryption key */ - uint64_t current_time = 0; - uint8_t ticket_key_name[16] = "2016.07.26.15\0"; - - POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, 1)); - POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - POSIX_GUARD(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - return S2N_SUCCESS; -} - static S2N_RESULT s2n_setup_encrypted_ticket(struct s2n_connection *conn, struct s2n_stuffer *output) { conn->tls13_ticket_fields = (struct s2n_ticket_fields){ 0 }; @@ -470,7 +448,7 @@ int main(int argc, char **argv) struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); server_conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; @@ -509,7 +487,7 @@ int main(int argc, char **argv) struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); server_conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; diff --git a/tests/unit/s2n_quic_support_io_test.c b/tests/unit/s2n_quic_support_io_test.c index 5bb7b02d5f5..8a2b744cfb7 100644 --- a/tests/unit/s2n_quic_support_io_test.c +++ b/tests/unit/s2n_quic_support_io_test.c @@ -244,6 +244,87 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); EXPECT_SUCCESS(s2n_connection_free(conn)); }; + + /* Succeeds for a handshake message larger than the input buffer */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t actual_message_type = 0; + + /* Read a small message to initialize the input buffer */ + const size_t small_message_size = 10; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, small_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, small_message_size)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), small_message_size); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + const size_t max_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + EXPECT_TRUE(max_buffer_size > small_message_size); + + /* Read a large message to force the input buffer to resize */ + const size_t large_message_size = max_buffer_size + 10; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, large_message_size)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), large_message_size); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + const size_t resized_buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + EXPECT_TRUE(resized_buffer_size >= large_message_size); + + /* Read another message to check that the resize doesn't prevent future reads */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + }; + + /* Succeeds for multiple messages */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t actual_message_type = 0; + size_t expected_buffer_size = 0; + for (size_t i = 0; i < 100; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + EXPECT_OK(s2n_record_wipe(conn)); + + /* Ensure buffer size stays constant */ + const size_t buffer_size = s2n_stuffer_space_remaining(&conn->buffer_in); + if (i == 0) { + expected_buffer_size = buffer_size; + } + EXPECT_EQUAL(expected_buffer_size, buffer_size); + } + }; }; /* Functional Tests */ diff --git a/tests/unit/s2n_record_read_test.c b/tests/unit/s2n_record_read_test.c index 067de45dc7e..14ce9e5171c 100644 --- a/tests/unit/s2n_record_read_test.c +++ b/tests/unit/s2n_record_read_test.c @@ -25,6 +25,11 @@ int main(int argc, char *argv[]) { BEGIN_TEST(); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + /* Test s2n_sslv2_record_header_parse */ { const struct { @@ -159,5 +164,52 @@ int main(int argc, char *argv[]) }; }; + /* Ensure that the input buffer is wiped after failing to read a record */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair stuffer_pair = { 0 }, + s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&stuffer_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &stuffer_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Send some test data to the server. */ + uint8_t test_data[] = "hello world"; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t send_size = s2n_send(client, test_data, sizeof(test_data), &blocked); + EXPECT_EQUAL(send_size, sizeof(test_data)); + + /* Invalidate an encrypted byte to cause decryption to fail. */ + struct s2n_stuffer invalidation_stuffer = stuffer_pair.server_in; + uint8_t *first_byte = s2n_stuffer_raw_read(&invalidation_stuffer, 1); + EXPECT_NOT_NULL(first_byte); + *first_byte += 1; + + /* Receive the invalid data. */ + uint8_t buffer[sizeof(test_data)] = { 0 }; + ssize_t ret = s2n_recv(server, buffer, sizeof(buffer), &blocked); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_DECRYPT); + + /* Ensure that the invalid data has been wiped from the input buffer. */ + EXPECT_TRUE(s2n_stuffer_is_wiped(&server->in)); + } + END_TEST(); } diff --git a/tests/unit/s2n_recv_buffering_test.c b/tests/unit/s2n_recv_buffering_test.c new file mode 100644 index 00000000000..7feffa75871 --- /dev/null +++ b/tests/unit/s2n_recv_buffering_test.c @@ -0,0 +1,619 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "api/s2n.h" +#include "api/unstable/renegotiate.h" +#include "s2n_test.h" +#include "testlib/s2n_ktls_test_utils.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +struct s2n_recv_wrapper { + size_t count; + s2n_recv_fn *inner_recv; + void *inner_recv_ctx; +}; + +static int s2n_counting_read(void *io_context, uint8_t *buf, uint32_t len) +{ + struct s2n_recv_wrapper *context = (struct s2n_recv_wrapper *) io_context; + context->count++; + return context->inner_recv(context->inner_recv_ctx, buf, len); +} + +static S2N_RESULT s2n_connection_set_counting_read(struct s2n_connection *reader, + struct s2n_recv_wrapper *wrapper) +{ + /* We'd need to handle cleanup for managed IO */ + RESULT_ENSURE(!reader->managed_recv_io, S2N_ERR_SAFETY); + + wrapper->inner_recv = reader->recv; + reader->recv = s2n_counting_read; + wrapper->inner_recv_ctx = reader->recv_io_context; + reader->recv_io_context = wrapper; + wrapper->count = 0; + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t test_data[20] = "hello world"; + const size_t buffer_in_size = S2N_LARGE_FRAGMENT_LENGTH; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_config *multi_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(multi_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(multi_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(multi_config)); + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(multi_config, true)); + + /* Test: Read a single record */ + uint32_t test_record_size_val = 0; + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_recv_wrapper counter = { 0 }; + EXPECT_OK(s2n_connection_set_counting_read(server, &counter)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + test_record_size_val = s2n_stuffer_data_available(&io_pair.server_in); + EXPECT_TRUE(test_record_size_val > sizeof(test_data)); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + EXPECT_EQUAL(counter.count, 1); + } + const uint32_t test_record_size = test_record_size_val; + + /* Test: Read the handshake */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + } + + /* Test: Read a record larger than the input buffer */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + client->max_outgoing_fragment_length = UINT16_MAX; + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_recv_wrapper counter = { 0 }; + EXPECT_OK(s2n_connection_set_counting_read(server, &counter)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + DEFER_CLEANUP(struct s2n_blob max_fragment_buffer = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&max_fragment_buffer, S2N_LARGE_FRAGMENT_LENGTH)); + + /* Send a record that won't fit in the default input buffer */ + EXPECT_EQUAL( + s2n_send(client, max_fragment_buffer.data, max_fragment_buffer.size, &blocked), + max_fragment_buffer.size); + size_t record_size = s2n_stuffer_data_available(&io_pair.server_in); + size_t fragment_size = record_size - S2N_TLS_RECORD_HEADER_LENGTH; + EXPECT_TRUE(fragment_size > buffer_in_size); + + /* Test that the record can be received and the input buffer resized */ + EXPECT_EQUAL( + s2n_recv(server, max_fragment_buffer.data, max_fragment_buffer.size, &blocked), + max_fragment_buffer.size); + EXPECT_TRUE(s2n_stuffer_space_remaining(&server->buffer_in) > fragment_size); + /* The header fits on the first read, but the rest of the data doesn't. + * We need a (large) shift + read to get the rest of the data. + */ + EXPECT_EQUAL(counter.count, 2); + + /* Check that another record can be received afterwards */ + uint8_t buffer[sizeof(test_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + EXPECT_EQUAL(counter.count, 3); + } + + /* Test: Read multiple small records */ + for (size_t greedy = 0; greedy <= 1; greedy++) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + if (greedy) { + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + } + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_recv_wrapper counter = { 0 }; + EXPECT_OK(s2n_connection_set_counting_read(server, &counter)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + for (size_t i = 1; i <= sizeof(test_data); i++) { + EXPECT_EQUAL(s2n_send(client, test_data, i, &blocked), i); + } + + uint8_t buffer[sizeof(test_data)] = { 0 }; + for (size_t i = 1; i <= sizeof(test_data); i++) { + EXPECT_EQUAL(s2n_recv(server, buffer, i, &blocked), i); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, i); + + if (greedy) { + /* All our small records combined are smaller than the maximum + * TLS record size, so they should all be buffered immediately. + * Only one read is ever necessary. + */ + EXPECT_EQUAL(counter.count, 1); + } else { + /* We call recv twice for every record */ + EXPECT_EQUAL(counter.count, i * 2); + } + } + + /* The input buffer size does not change with greedy vs not greedy */ + EXPECT_EQUAL(server->buffer_in.blob.allocated, buffer_in_size); + + /* If all data is consumed, the input buffer can be released */ + EXPECT_SUCCESS(s2n_connection_release_buffers(server)); + EXPECT_EQUAL(server->buffer_in.blob.allocated, 0); + } + + /* Test: Read multiple small records with "multi_record" enabled */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, multi_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, multi_config)); + + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_recv_wrapper counter = { 0 }; + EXPECT_OK(s2n_connection_set_counting_read(server, &counter)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + for (size_t i = 0; i < sizeof(test_data); i++) { + EXPECT_EQUAL(s2n_send(client, test_data + i, 1, &blocked), 1); + } + + uint8_t buffer[sizeof(test_data)] = { 0 }; + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + EXPECT_EQUAL(counter.count, 1); + } + + /* Test: Read the rest of a partial record */ + for (size_t i = 0; i < test_record_size; i++) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_recv_wrapper counter = { 0 }; + EXPECT_OK(s2n_connection_set_counting_read(server, &counter)); + + size_t expected_count = 0; + + /* Test: manually copy some of the record into the read buffer */ + { + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.server_in), test_record_size); + EXPECT_SUCCESS(s2n_stuffer_copy(&io_pair.server_in, &server->buffer_in, i)); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + expected_count++; + EXPECT_EQUAL(counter.count, expected_count); + } + + /* Test: force the first recv to return partial data */ + { + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.server_in), 0); + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.server_in), test_record_size); + + io_pair.server_in.write_cursor -= (test_record_size - i); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server, buffer, sizeof(buffer), &blocked), + S2N_ERR_IO_BLOCKED); + expected_count++; + /* If the first call returns any data, then a second call is made. + * The second call blocks. */ + if (i != 0) { + expected_count++; + } + EXPECT_EQUAL(counter.count, expected_count); + + io_pair.server_in.write_cursor += (test_record_size - i); + + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + expected_count++; + EXPECT_EQUAL(counter.count, expected_count); + } + } + + /* Test: read a single record one byte at a time */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_recv_wrapper counter = { 0 }; + EXPECT_OK(s2n_connection_set_counting_read(server, &counter)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.server_in), 0); + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.server_in), test_record_size); + io_pair.server_in.write_cursor -= test_record_size; + + size_t expected_count = 0; + uint8_t buffer[sizeof(test_data)] = { 0 }; + for (size_t i = 1; i < test_record_size; i++) { + /* Reads no additional data-- just blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server, buffer, sizeof(buffer), &blocked), + S2N_ERR_IO_BLOCKED); + expected_count++; + EXPECT_EQUAL(counter.count, expected_count); + + /* Reads the next byte, then blocks again */ + io_pair.server_in.write_cursor++; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server, buffer, sizeof(buffer), &blocked), + S2N_ERR_IO_BLOCKED); + expected_count += 2; + EXPECT_EQUAL(counter.count, expected_count); + } + + /* Reads the final byte and succeeds */ + io_pair.server_in.write_cursor++; + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + expected_count++; + EXPECT_EQUAL(counter.count, expected_count); + } + + /* Test: Read into a buffer that already contains data from a previous read */ + const struct { + /* The offset the current record should begin at */ + uint16_t offset; + /* Assert that shifting occurred if necessary */ + uint16_t final_offset; + /* Most offsets result in a single read */ + uint8_t reads; + } test_offsets[] = { + /* Basic small offsets: single read, no shifting */ + { .offset = 0, .final_offset = test_record_size, .reads = 1 }, + { .offset = 10, .final_offset = 10 + test_record_size, .reads = 1 }, + { .offset = 1000, .final_offset = 1000 + test_record_size, .reads = 1 }, + /* Exactly enough space remaining in the buffer, so no shift or second read. + * We wipe the buffer after: the extra byte we add to avoid the wipe isn't + * read because we read exactly as much data as we need. + */ + { + .offset = buffer_in_size - test_record_size, + .final_offset = 0, + .reads = 1, + }, + /* If we have enough space in the buffer for the next header, + * but not enough for the next fragment, then we must still read twice. + */ + { + .offset = buffer_in_size - S2N_TLS_RECORD_HEADER_LENGTH, + .final_offset = test_record_size - S2N_TLS_RECORD_HEADER_LENGTH, + .reads = 2, + }, + { + .offset = buffer_in_size - S2N_TLS_RECORD_HEADER_LENGTH - 1, + .final_offset = test_record_size - S2N_TLS_RECORD_HEADER_LENGTH, + .reads = 2, + }, + /* Not enough space in the buffer for the header or the fragment. + * We have to shift but don't need a second read. + */ + { .offset = buffer_in_size - 3, .final_offset = test_record_size, .reads = 1 }, + { .offset = buffer_in_size - 1, .final_offset = test_record_size, .reads = 1 }, + { .offset = buffer_in_size, .final_offset = test_record_size, .reads = 1 }, + }; + for (size_t i = 0; i < s2n_array_len(test_offsets); i++) { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_recv_wrapper counter = { 0 }; + EXPECT_OK(s2n_connection_set_counting_read(server, &counter)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.server_in), test_record_size); + /* Write one more byte so that we won't wipe buffer_in after the read. + * This will let us better examine the state of the buffer. + */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&io_pair.server_in, 0)); + + uint16_t offset = test_offsets[i].offset; + EXPECT_SUCCESS(s2n_stuffer_wipe(&server->buffer_in)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&server->buffer_in, offset)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&server->buffer_in, offset)); + if (offset < buffer_in_size) { + /* Preemptively copy one byte of the next record into buffer_in. + * If we don't do this, we just wipe buffer_in before the read, + * making this test trivial. + */ + EXPECT_SUCCESS(s2n_stuffer_copy(&io_pair.server_in, &server->buffer_in, 1)); + } + + uint8_t buffer[sizeof(test_data)] = { 0 }; + + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + EXPECT_EQUAL(counter.count, test_offsets[i].reads); + uint32_t expected_final_offset = test_offsets[i].final_offset; + /* If there is an offset, consider the extra byte we added to avoid the final wipe. */ + if (expected_final_offset != 0) { + expected_final_offset++; + } + EXPECT_EQUAL(server->buffer_in.write_cursor, expected_final_offset); + } + + /* Test: Toggle recv_greedy while reading */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t buffer[sizeof(test_data)] = { 0 }; + + /* Send many records */ + const size_t records_count = 100; + for (size_t i = 0; i < records_count; i++) { + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + } + + for (size_t i = 0; i < records_count / 2; i++) { + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + EXPECT_TRUE(s2n_stuffer_data_available(&server->buffer_in)); + + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, false)); + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + } + } + + /* Test: s2n_connection_release_buffers with data remaining in buffer_in */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in)); + + /* Send two records */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + /* Only consume a partial record */ + io_pair.server_in.write_cursor = test_record_size / 2; + uint8_t buffer[sizeof(test_data)] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO( + s2n_recv(server, buffer, sizeof(test_data), &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_TRUE(s2n_stuffer_data_available(&server->in)); + EXPECT_FAILURE_WITH_ERRNO( + s2n_connection_release_buffers(server), + S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); + + /* Consume the full first record */ + /* cppcheck-suppress redundantAssignment */ + io_pair.server_in.write_cursor = test_record_size * 2; + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + + /* Release buffers */ + EXPECT_TRUE(s2n_stuffer_data_available(&server->buffer_in)); + EXPECT_SUCCESS(s2n_connection_release_buffers(server)); + EXPECT_TRUE(s2n_stuffer_data_available(&server->buffer_in)); + + /* Consume the full second record */ + EXPECT_EQUAL(s2n_recv(server, buffer, sizeof(buffer), &blocked), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(buffer, test_data, sizeof(test_data)); + } + + /* Test: s2n_peek_buffered */ + { + EXPECT_EQUAL(s2n_peek_buffered(NULL), 0); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, multi_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, multi_config)); + + struct { + uint32_t read_size; + uint32_t expect_available; + uint32_t expect_buffered; + } test_cases[] = { + { + .read_size = 1, + .expect_available = sizeof(test_data) - 1, + .expect_buffered = test_record_size, + }, + { + .read_size = sizeof(test_data) - 1, + .expect_available = 1, + .expect_buffered = test_record_size, + }, + { + .read_size = sizeof(test_data), + .expect_available = 0, + .expect_buffered = test_record_size, + }, + { + .read_size = sizeof(test_data) + 1, + .expect_available = sizeof(test_data) - 1, + .expect_buffered = 0, + }, + }; + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t buffer[sizeof(test_data) * 2] = { 0 }; + + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(s2n_send(client, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + uint32_t read_size = test_cases[i].read_size; + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(server, true)); + EXPECT_EQUAL(s2n_recv(server, buffer, read_size, &blocked), read_size); + EXPECT_EQUAL(s2n_peek_buffered(server), test_cases[i].expect_buffered); + EXPECT_EQUAL(s2n_peek(server), test_cases[i].expect_available); + + EXPECT_SUCCESS(s2n_connection_wipe(client)); + EXPECT_SUCCESS(s2n_connection_wipe(server)); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_renegotiate_test.c b/tests/unit/s2n_renegotiate_test.c index d599b1e949f..80a880daadc 100644 --- a/tests/unit/s2n_renegotiate_test.c +++ b/tests/unit/s2n_renegotiate_test.c @@ -452,6 +452,41 @@ int main(int argc, char *argv[]) EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); }; + + /* Wipe with next record buffered allowed, and data preserved */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_recv_buffering(client, true)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Write two records, but only receive one. + * Due to recv buffering, the second record will be read and buffered + * at the same time as the first record, but not processed yet. + */ + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_send(server, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_peek(client), 0); + EXPECT_TRUE(s2n_stuffer_data_available(&client->buffer_in)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client)); + + /* The second record is still available to read after the wipe */ + EXPECT_EQUAL(s2n_recv(client, recv_buffer, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; }; /* Test the basic renegotiation mechanism with a variety of connection parameters. diff --git a/tests/unit/s2n_security_policies_test.c b/tests/unit/s2n_security_policies_test.c index 8a3c1b0989f..6efd9340796 100644 --- a/tests/unit/s2n_security_policies_test.c +++ b/tests/unit/s2n_security_policies_test.c @@ -22,6 +22,7 @@ #include "testlib/s2n_testlib.h" #include "tls/s2n_kem.h" #include "tls/s2n_signature_algorithms.h" +#include "tls/s2n_tls.h" static S2N_RESULT s2n_test_security_policies_compatible(const struct s2n_security_policy *policy, const char *default_policy, struct s2n_cert_chain_and_key *cert_chain) @@ -460,6 +461,7 @@ int main(int argc, char **argv) "20190121", "20190122", "20201021", + "20240331", "test_all_ecdsa", "test_ecdsa_priority", "test_all_tls12", @@ -478,6 +480,7 @@ int main(int argc, char **argv) "20190801", "20190802", "KMS-TLS-1-2-2023-06", + "20230317", /* CloudFront viewer facing */ "CloudFront-SSL-v-3", "CloudFront-TLS-1-0-2014", @@ -602,7 +605,7 @@ int main(int argc, char **argv) EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); - EXPECT_EQUAL(config->security_policy, &security_policy_default_tls13); + EXPECT_EQUAL(config->security_policy, &security_policy_20240417); EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_20210831); EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); @@ -728,7 +731,7 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_EQUAL(security_policy, &security_policy_default_tls13); + EXPECT_EQUAL(security_policy, &security_policy_20240417); EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_20210831); EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_null); EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20200207); @@ -818,7 +821,6 @@ int main(int argc, char **argv) /* If scheme will be used for pre-tls1.3 */ if (min_version < S2N_TLS13) { - EXPECT_NULL(scheme->signature_curve); EXPECT_NOT_EQUAL(scheme->sig_alg, S2N_SIGNATURE_RSA_PSS_PSS); } } @@ -973,6 +975,48 @@ int main(int argc, char **argv) EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "default_tls13", rsa_pss_chain_and_key)); EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "20230317", rsa_pss_chain_and_key)); } + + if (s2n_is_tls13_fully_supported()) { + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, + "test_all_tls13", rsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, + "test_all_tls13", rsa_pss_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, + "test_all_tls13", ecdsa_chain_and_key)); + } + }; + + /* 20240331 */ + { + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "default", rsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "default_tls13", rsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "default_fips", rsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "20230317", rsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "20240331", rsa_chain_and_key)); + + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "default_tls13", ecdsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "default_fips", ecdsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "20230317", ecdsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20240331, + "20240331", ecdsa_chain_and_key)); + + /* Can't negotiate TLS1.3 */ + EXPECT_ERROR_WITH_ERRNO( + s2n_test_security_policies_compatible(&security_policy_20240331, + "test_all_tls13", rsa_chain_and_key), + S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_ERROR_WITH_ERRNO( + s2n_test_security_policies_compatible(&security_policy_20240331, + "test_all_tls13", ecdsa_chain_and_key), + S2N_ERR_CIPHER_NOT_SUPPORTED); }; }; diff --git a/tests/unit/s2n_self_talk_io_mem_test.c b/tests/unit/s2n_self_talk_io_mem_test.c index 2453150b03c..f28a1d1f8fc 100644 --- a/tests/unit/s2n_self_talk_io_mem_test.c +++ b/tests/unit/s2n_self_talk_io_mem_test.c @@ -184,8 +184,8 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); /* All IO buffers empty */ - EXPECT_EQUAL(client_conn->in.blob.size, 0); - EXPECT_EQUAL(server_conn->in.blob.size, 0); + EXPECT_EQUAL(client_conn->buffer_in.blob.size, 0); + EXPECT_EQUAL(server_conn->buffer_in.blob.size, 0); EXPECT_EQUAL(client_conn->out.blob.size, 0); EXPECT_EQUAL(server_conn->out.blob.size, 0); @@ -199,16 +199,16 @@ int main(int argc, char **argv) EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); /* All IO buffers not empty */ - EXPECT_NOT_EQUAL(client_conn->in.blob.size, 0); - EXPECT_NOT_EQUAL(server_conn->in.blob.size, 0); + EXPECT_NOT_EQUAL(client_conn->buffer_in.blob.size, 0); + EXPECT_NOT_EQUAL(server_conn->buffer_in.blob.size, 0); EXPECT_NOT_EQUAL(client_conn->out.blob.size, 0); EXPECT_NOT_EQUAL(server_conn->out.blob.size, 0); /* Wipe connections */ EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); - EXPECT_EQUAL(client_conn->in.blob.size, 0); - EXPECT_EQUAL(server_conn->in.blob.size, 0); + EXPECT_EQUAL(client_conn->buffer_in.blob.size, 0); + EXPECT_EQUAL(server_conn->buffer_in.blob.size, 0); EXPECT_EQUAL(client_conn->out.blob.size, 0); EXPECT_EQUAL(server_conn->out.blob.size, 0); @@ -244,9 +244,9 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); /* all IO buffers should be empty after the handshake */ - EXPECT_EQUAL(client_conn->in.blob.size, 0); + EXPECT_EQUAL(client_conn->buffer_in.blob.size, 0); EXPECT_EQUAL(client_conn->out.blob.size, 0); - EXPECT_EQUAL(server_conn->in.blob.size, 0); + EXPECT_EQUAL(server_conn->buffer_in.blob.size, 0); EXPECT_EQUAL(server_conn->out.blob.size, 0); /* block the server from sending */ @@ -283,13 +283,13 @@ int main(int argc, char **argv) EXPECT_EQUAL(s2n_recv(client_conn, &buf, s2n_array_len(buf) / 2, &blocked), s2n_array_len(buf) / 2); /* the `in` buffer should not be freed until it's completely flushed to the application */ - EXPECT_NOT_EQUAL(client_conn->in.blob.size, 0); + EXPECT_NOT_EQUAL(client_conn->buffer_in.blob.size, 0); /* Receive the second half of the payload on the second call */ EXPECT_EQUAL(s2n_recv(client_conn, &buf, s2n_array_len(buf) / 2, &blocked), s2n_array_len(buf) / 2); /* at this point the application has received the full message and the `in` buffer should be freed */ - EXPECT_EQUAL(client_conn->in.blob.size, 0); + EXPECT_EQUAL(client_conn->buffer_in.blob.size, 0); }; EXPECT_SUCCESS(s2n_config_free(config)); diff --git a/tests/unit/s2n_self_talk_session_resumption_test.c b/tests/unit/s2n_self_talk_session_resumption_test.c index de354884c30..6b5621baa1c 100644 --- a/tests/unit/s2n_self_talk_session_resumption_test.c +++ b/tests/unit/s2n_self_talk_session_resumption_test.c @@ -32,7 +32,7 @@ struct s2n_early_data_test_case { bool ticket_supported; bool client_supported; bool server_supported; - bool expect_success; + bool expect_early_data; }; static S2N_RESULT s2n_assert_tickets_sent(struct s2n_connection *conn, uint16_t expected_tickets_sent) @@ -88,30 +88,6 @@ static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, st return S2N_SUCCESS; } -static int s2n_setup_test_ticket_key(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - /** - *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 - *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 - *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) - **/ - S2N_BLOB_FROM_HEX(ticket_key, - "077709362c2e32df0ddc3f0dc47bba63" - "90b6c73bb50f9c3122ec844ad7c2b3e5"); - - /* Set up encryption key */ - uint64_t current_time = 0; - uint8_t ticket_key_name[16] = "2016.07.26.15\0"; - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - - return S2N_SUCCESS; -} - static S2N_RESULT s2n_test_issue_new_session_ticket(struct s2n_connection *server_conn, struct s2n_connection *client_conn, const struct s2n_early_data_test_case *early_data_case) { @@ -165,11 +141,11 @@ static S2N_RESULT s2n_test_negotiate(struct s2n_connection *server_conn, struct RESULT_GUARD(s2n_negotiate_test_server_and_client_with_early_data(server_conn, client_conn, &early_data_send, &early_data_recv)); - if (early_data_case->expect_success) { + if (early_data_case->expect_early_data) { RESULT_ENSURE_EQ(early_data_recv.size, sizeof(early_data)); EXPECT_BYTEARRAY_EQUAL(early_data_recv.data, early_data, sizeof(early_data)); } else { - RESULT_ENSURE_EQ(early_data_recv.size, sizeof(empty_data)); + RESULT_ENSURE_EQ(early_data_recv.size, 0); EXPECT_BYTEARRAY_EQUAL(early_data_recv.data, empty_data, sizeof(empty_data)); } @@ -218,21 +194,24 @@ int main(int argc, char **argv) size_t test_case_i = 0; struct s2n_early_data_test_case early_data_test_cases[2 * 2 * 2] = { 0 }; for (size_t ticket_supported = 0; ticket_supported < 2; ticket_supported++) { - early_data_test_cases[test_case_i].ticket_supported = ticket_supported; for (size_t client_supported = 0; client_supported < 2; client_supported++) { - early_data_test_cases[test_case_i].client_supported = client_supported; for (size_t server_supported = 0; server_supported < 2; server_supported++) { + EXPECT_TRUE(test_case_i < s2n_array_len(early_data_test_cases)); + early_data_test_cases[test_case_i].ticket_supported = ticket_supported; + early_data_test_cases[test_case_i].client_supported = client_supported; early_data_test_cases[test_case_i].server_supported = server_supported; - early_data_test_cases[test_case_i].expect_success = client_supported && server_supported && ticket_supported; + early_data_test_cases[test_case_i].expect_early_data = client_supported && server_supported && ticket_supported; + test_case_i++; } } - test_case_i++; } + EXPECT_EQUAL(test_case_i, s2n_array_len(early_data_test_cases)); + /* For some session resumption test cases, we don't want to test or don't care about 0-RTT */ const struct s2n_early_data_test_case no_early_data = { .client_supported = false, .server_supported = false, - .expect_success = false + .expect_early_data = false }; /* Setup server config */ @@ -245,7 +224,7 @@ int main(int argc, char **argv) S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, true)); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(server_config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(server_config)); /* Setup TLS1.2 server config */ struct s2n_config *tls12_server_config = s2n_config_new(); @@ -257,7 +236,7 @@ int main(int argc, char **argv) S2N_DEFAULT_TEST_PRIVATE_KEY)); EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_server_config, tls12_chain_and_key)); EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_server_config, true)); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(tls12_server_config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(tls12_server_config)); /* Setup TLS1.3 client config */ struct s2n_config *tls13_client_config = s2n_config_new(); @@ -478,7 +457,7 @@ int main(int argc, char **argv) for (size_t early_data_i = 0; early_data_i < s2n_array_len(early_data_test_cases); early_data_i++) { struct s2n_early_data_test_case early_data_case = early_data_test_cases[early_data_i]; /* Early data is never sent in TLS1.2 (or in a full handshake) */ - early_data_case.expect_success = false; + early_data_case.expect_early_data = false; struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); @@ -592,7 +571,7 @@ int main(int argc, char **argv) for (size_t early_data_i = 0; early_data_i < s2n_array_len(early_data_test_cases); early_data_i++) { struct s2n_early_data_test_case early_data_case = early_data_test_cases[early_data_i]; /* Never use early data on a HRR */ - early_data_case.expect_success = false; + early_data_case.expect_early_data = false; struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); diff --git a/tests/unit/s2n_server_cert_request_test.c b/tests/unit/s2n_server_cert_request_test.c index 828eddfceb5..fbdd6392c92 100644 --- a/tests/unit/s2n_server_cert_request_test.c +++ b/tests/unit/s2n_server_cert_request_test.c @@ -60,7 +60,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); - s2n_cert_req_send(server_conn); + EXPECT_SUCCESS(s2n_cert_req_send(server_conn)); struct s2n_stuffer *in = &server_conn->handshake.io; uint8_t cert_types_len = 0; @@ -88,7 +88,7 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); EXPECT_SUCCESS(s2n_config_enable_cert_req_dss_legacy_compat(server_config)); - s2n_cert_req_send(server_conn); + EXPECT_SUCCESS(s2n_cert_req_send(server_conn)); struct s2n_stuffer *in = &server_conn->handshake.io; uint8_t cert_types_len = 0; @@ -105,6 +105,78 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_free(server_conn)); }; + /* Test: certificate_authorities supported */ + { + /* Test: no cert_authorities sent by default */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_cert_req_send(conn)); + struct s2n_stuffer *output = &conn->handshake.io; + + uint8_t cert_types_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(output, &cert_types_len)); + EXPECT_TRUE(cert_types_len > 0); + EXPECT_SUCCESS(s2n_stuffer_skip_read(output, cert_types_len)); + + uint16_t sig_algs_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(output, &sig_algs_len)); + EXPECT_TRUE(sig_algs_len > 0); + EXPECT_SUCCESS(s2n_stuffer_skip_read(output, sig_algs_len)); + + uint16_t cert_authorities_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(output, &cert_authorities_len)); + EXPECT_EQUAL(cert_authorities_len, 0); + + EXPECT_EQUAL(s2n_stuffer_data_available(output), 0); + }; + + /* Test: cert_authorities sent if configured */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* If we use TLS1.1 instead of TLS1.2, we don't need to worry about + * skipping the signature algorithms. + */ + conn->actual_protocol_version = S2N_TLS11; + + const uint8_t ca_data[] = "these are my CAs"; + EXPECT_SUCCESS(s2n_alloc(&config->cert_authorities, sizeof(ca_data))); + EXPECT_MEMCPY_SUCCESS(config->cert_authorities.data, ca_data, sizeof(ca_data)); + + EXPECT_SUCCESS(s2n_cert_req_send(conn)); + struct s2n_stuffer *output = &conn->handshake.io; + + uint8_t cert_types_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(output, &cert_types_len)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(output, cert_types_len)); + + uint16_t cert_authorities_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(output, &cert_authorities_len)); + EXPECT_EQUAL(cert_authorities_len, sizeof(ca_data)); + + uint8_t *cert_authorities_data = s2n_stuffer_raw_read(output, cert_authorities_len); + EXPECT_NOT_NULL(cert_authorities_data); + EXPECT_BYTEARRAY_EQUAL(cert_authorities_data, ca_data, sizeof(ca_data)); + + EXPECT_EQUAL(s2n_stuffer_data_available(output), 0); + }; + }; + END_TEST(); - return 0; } diff --git a/tests/unit/s2n_server_new_session_ticket_test.c b/tests/unit/s2n_server_new_session_ticket_test.c index b299964fefc..49fb0eb04f5 100644 --- a/tests/unit/s2n_server_new_session_ticket_test.c +++ b/tests/unit/s2n_server_new_session_ticket_test.c @@ -58,30 +58,6 @@ static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, st return S2N_SUCCESS; } -static int s2n_setup_test_ticket_key(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - /** - *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 - *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 - *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) - **/ - S2N_BLOB_FROM_HEX(ticket_key, - "077709362c2e32df0ddc3f0dc47bba63" - "90b6c73bb50f9c3122ec844ad7c2b3e5"); - - /* Set up encryption key */ - uint64_t current_time = 0; - uint8_t ticket_key_name[16] = "2016.07.26.15\0"; - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - - return S2N_SUCCESS; -} - static int s2n_setup_test_resumption_secret(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); @@ -117,7 +93,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -189,7 +165,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -219,7 +195,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -261,7 +237,7 @@ int main(int argc, char **argv) struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); EXPECT_NOT_NULL(conn); @@ -300,7 +276,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(config); EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); EXPECT_NOT_NULL(server_conn); @@ -655,7 +631,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(server_conn); EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); @@ -688,7 +664,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(config); EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); EXPECT_NOT_NULL(client_conn); @@ -726,7 +702,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(config); EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); EXPECT_NOT_NULL(client_conn); @@ -854,7 +830,7 @@ int main(int argc, char **argv) DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); @@ -882,7 +858,7 @@ int main(int argc, char **argv) { struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); EXPECT_NOT_NULL(conn); @@ -916,7 +892,7 @@ int main(int argc, char **argv) DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); @@ -949,7 +925,7 @@ int main(int argc, char **argv) struct s2n_connection *conn = NULL; EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -998,7 +974,7 @@ int main(int argc, char **argv) struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); EXPECT_NOT_NULL(conn); @@ -1135,7 +1111,7 @@ int main(int argc, char **argv) EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); EXPECT_NOT_NULL(config = s2n_config_new()); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); conn->actual_protocol_version = S2N_TLS13; @@ -1176,7 +1152,7 @@ int main(int argc, char **argv) { struct s2n_config *config = s2n_config_new(); EXPECT_NOT_NULL(config); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); EXPECT_NOT_NULL(conn); @@ -1285,7 +1261,7 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(config)); EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); uint16_t tickets_to_send = 5; diff --git a/tests/unit/s2n_signature_algorithms_test.c b/tests/unit/s2n_signature_algorithms_test.c index 63bea8097bf..ebcfa015a00 100644 --- a/tests/unit/s2n_signature_algorithms_test.c +++ b/tests/unit/s2n_signature_algorithms_test.c @@ -25,6 +25,7 @@ #include "tls/s2n_connection.h" #include "tls/s2n_security_policies.h" #include "tls/s2n_signature_scheme.h" +#include "tls/s2n_tls.h" #define LENGTH (s2n_array_len(test_signature_schemes)) #define STUFFER_SIZE (LENGTH * TLS_SIGNATURE_SCHEME_LEN + 10) @@ -33,12 +34,27 @@ #define ECDSA_CIPHER_SUITE &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha #define TLS13_CIPHER_SUITE &s2n_tls13_aes_128_gcm_sha256 +/* The only TLS1.3-only signature schemes are RSA-PSS-PSS, which + * are difficult to test with due to mixed libcrypto support. + * Use a test scheme instead. + */ +const struct s2n_signature_scheme s2n_test_tls13_ecdsa_sha384 = { + .iana_value = TLS_SIGNATURE_SCHEME_ECDSA_SHA384, + .hash_alg = S2N_HASH_SHA384, + .sig_alg = S2N_SIGNATURE_ECDSA, + .libcrypto_nid = NID_ecdsa_with_SHA384, + .signature_curve = &s2n_ecc_curve_secp384r1, + /* Only supports TLS1.3 for testing */ + .minimum_protocol_version = S2N_TLS13, +}; + const struct s2n_signature_scheme *const test_signature_schemes[] = { - &s2n_ecdsa_secp384r1_sha384, + &s2n_test_tls13_ecdsa_sha384, &s2n_rsa_pkcs1_sha256, &s2n_rsa_pkcs1_sha224, &s2n_rsa_pkcs1_sha1, &s2n_ecdsa_sha1, + &s2n_ecdsa_sha256, }; const struct s2n_signature_preferences test_preferences = { @@ -141,7 +157,7 @@ int main(int argc, char **argv) EXPECT_EQUAL(size, s2n_stuffer_data_available(&result)); for (size_t i = 0; i < s2n_array_len(test_signature_schemes); i++) { - if (test_signature_schemes[i] != &s2n_ecdsa_secp384r1_sha384) { + if (test_signature_schemes[i] != &s2n_test_tls13_ecdsa_sha384) { uint16_t iana_value = 0; EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &iana_value)); EXPECT_EQUAL(iana_value, test_signature_schemes[i]->iana_value); @@ -170,6 +186,7 @@ int main(int argc, char **argv) for (size_t i = 0; i < s2n_array_len(test_signature_schemes); i++) { EXPECT_EQUAL(signatures.iana_list[i], test_signature_schemes[i]->iana_value); } + EXPECT_EQUAL(s2n_stuffer_data_available(&result), 0); }; /* Test: do not send TLS1.2 signature schemes if QUIC enabled */ @@ -189,9 +206,14 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &size)); EXPECT_EQUAL(size, s2n_stuffer_data_available(&result)); - uint16_t iana_value = 0; - EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &iana_value)); - EXPECT_EQUAL(iana_value, s2n_ecdsa_secp384r1_sha384.iana_value); + for (size_t i = 0; i < s2n_array_len(test_signature_schemes); i++) { + if (test_signature_schemes[i]->maximum_protocol_version == 0 + || test_signature_schemes[i]->maximum_protocol_version >= S2N_TLS13) { + uint16_t iana_value = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &iana_value)); + EXPECT_EQUAL(iana_value, test_signature_schemes[i]->iana_value); + } + } EXPECT_EQUAL(s2n_stuffer_data_available(&result), 0); }; }; @@ -218,7 +240,6 @@ int main(int argc, char **argv) * just not going to choose them. */ const struct s2n_signature_scheme *test_schemes[] = { - &s2n_ecdsa_secp384r1_sha384, &s2n_ecdsa_sha384, &s2n_rsa_pss_rsae_sha256, &s2n_rsa_pss_pss_sha256, @@ -414,7 +435,7 @@ int main(int argc, char **argv) /* Test: scheme not valid for higher protocol version */ { /* Valid TLS1.3 ECDSA sig schemes include associated curves */ - const struct s2n_signature_scheme *invalid = &s2n_ecdsa_sha256; + const struct s2n_signature_scheme *invalid = &s2n_ecdsa_sha224; DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); @@ -441,8 +462,7 @@ int main(int argc, char **argv) /* Test: scheme not valid for lower protocol version */ { - /* Valid TLS1.2 ECDSA sig schemes do not include associated curves */ - const struct s2n_signature_scheme *invalid = &s2n_ecdsa_secp384r1_sha384; + const struct s2n_signature_scheme *invalid = &s2n_test_tls13_ecdsa_sha384; DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); @@ -472,7 +492,7 @@ int main(int argc, char **argv) /* No SHA1 signature schemes for TLS1.3 actually exist. * Create one for testing. */ - struct s2n_signature_scheme sha1_tls13_scheme = s2n_ecdsa_secp384r1_sha384; + struct s2n_signature_scheme sha1_tls13_scheme = s2n_ecdsa_sha384; sha1_tls13_scheme.hash_alg = s2n_ecdsa_sha1.hash_alg; const struct s2n_signature_scheme *invalid = &sha1_tls13_scheme; @@ -494,7 +514,7 @@ int main(int argc, char **argv) S2N_ERR_NO_VALID_SIGNATURE_SCHEME); /* Succeeds without SHA1 */ - sha1_tls13_scheme.hash_alg = s2n_ecdsa_secp384r1_sha384.hash_alg; + sha1_tls13_scheme.hash_alg = s2n_ecdsa_sha384.hash_alg; EXPECT_OK(s2n_signature_algorithm_select(conn)); }; @@ -600,8 +620,8 @@ int main(int argc, char **argv) * extension is used instead. See https://github.com/aws/s2n-tls/issues/4274 */ { - const struct s2n_signature_scheme *ecdsa384 = &s2n_ecdsa_secp384r1_sha384; - const struct s2n_signature_scheme *ecdsa256 = &s2n_ecdsa_secp256r1_sha256; + const struct s2n_signature_scheme *ecdsa384 = &s2n_ecdsa_sha384; + const struct s2n_signature_scheme *ecdsa256 = &s2n_ecdsa_sha256; DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); @@ -637,16 +657,16 @@ int main(int argc, char **argv) conn->secure->cipher_suite = TLS13_CIPHER_SUITE; EXPECT_SUCCESS(s2n_connection_set_config(conn, client_ecdsa_config)); - const struct s2n_signature_scheme *expected = &s2n_ecdsa_secp384r1_sha384; + const struct s2n_signature_scheme *expected = &s2n_ecdsa_sha384; const struct s2n_signature_scheme *schemes[] = { /* No RSA certificates */ &s2n_rsa_pss_rsae_sha256, &s2n_rsa_pss_pss_sha256, &s2n_rsa_pkcs1_sha256, /* Only valid for TLS1.2 */ - &s2n_ecdsa_sha384, + &s2n_ecdsa_sha224, /* Wrong curve */ - &s2n_ecdsa_secp256r1_sha256, + &s2n_ecdsa_sha256, expected }; @@ -691,16 +711,14 @@ int main(int argc, char **argv) /* Test: no schemes offered by the peer */ { - const struct s2n_signature_scheme *ecdsa_not_default_tls12 = &s2n_ecdsa_sha384; - const struct s2n_signature_scheme *ecdsa_not_default_tls13 = &s2n_ecdsa_secp384r1_sha384; + const struct s2n_signature_scheme *ecdsa_not_default = &s2n_ecdsa_sha384; const struct s2n_signature_scheme *rsa_not_default = &s2n_rsa_pkcs1_sha256; /* Test: defaults allowed by security policy */ { /* We should need to skip valid non-default schemes to choose the defaults */ const struct s2n_signature_scheme *schemes_with_defaults[] = { - ecdsa_not_default_tls12, - ecdsa_not_default_tls13, + ecdsa_not_default, rsa_not_default, rsa_default, ecdsa_default @@ -730,18 +748,17 @@ int main(int argc, char **argv) conn->actual_protocol_version = S2N_TLS13; conn->secure->cipher_suite = TLS13_CIPHER_SUITE; EXPECT_OK(s2n_signature_algorithm_select(conn)); - EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, ecdsa_not_default_tls13); + EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, ecdsa_not_default); } /* Test: defaults not allowed by security policy */ { const struct s2n_signature_scheme *schemes_without_defaults[] = { - ecdsa_not_default_tls12, - ecdsa_not_default_tls13, + ecdsa_not_default, rsa_not_default, /* Add some more, less preferred non-defaults. * We only choose the most preferred though. */ - &s2n_ecdsa_secp384r1_sha384, + &s2n_ecdsa_sha512, &s2n_ecdsa_sha256, &s2n_rsa_pss_rsae_sha384, }; @@ -758,7 +775,7 @@ int main(int argc, char **argv) conn->actual_protocol_version = S2N_TLS12; conn->secure->cipher_suite = ECDSA_CIPHER_SUITE; EXPECT_OK(s2n_signature_algorithm_select(conn)); - EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, ecdsa_not_default_tls12); + EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, ecdsa_not_default); /* TLS1.2 with RSA does not choose default */ conn->actual_protocol_version = S2N_TLS12; @@ -770,21 +787,21 @@ int main(int argc, char **argv) conn->actual_protocol_version = S2N_TLS13; conn->secure->cipher_suite = TLS13_CIPHER_SUITE; EXPECT_OK(s2n_signature_algorithm_select(conn)); - EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, ecdsa_not_default_tls13); + EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, ecdsa_not_default); }; /* Test: skip invalid fallback candidates to choose valid one */ { - const struct s2n_signature_scheme *expected = &s2n_ecdsa_secp384r1_sha384; + const struct s2n_signature_scheme *expected = &s2n_ecdsa_sha384; const struct s2n_signature_scheme *schemes[] = { /* No RSA certificates */ &s2n_rsa_pss_rsae_sha256, &s2n_rsa_pss_pss_sha256, &s2n_rsa_pkcs1_sha256, /* Only valid for TLS1.2 */ - &s2n_ecdsa_sha384, + &s2n_ecdsa_sha224, /* Wrong curve */ - &s2n_ecdsa_secp256r1_sha256, + &s2n_ecdsa_sha256, expected }; @@ -808,8 +825,7 @@ int main(int argc, char **argv) * the peer offered no signature schemes at all. */ { - const struct s2n_signature_scheme *ecdsa_not_default_tls12 = &s2n_ecdsa_sha384; - const struct s2n_signature_scheme *ecdsa_not_default_tls13 = &s2n_ecdsa_secp384r1_sha384; + const struct s2n_signature_scheme *ecdsa_not_default = &s2n_ecdsa_sha384; const struct s2n_signature_scheme *rsa_not_default = &s2n_rsa_pss_rsae_sha256; /* Test: TLS1.2 chooses defaults */ @@ -820,8 +836,7 @@ int main(int argc, char **argv) /* We should need to skip valid non-default schemes to choose the defaults */ const struct s2n_signature_scheme *local_schemes[] = { invalid_scheme, - ecdsa_not_default_tls12, - ecdsa_not_default_tls13, + ecdsa_not_default, rsa_not_default, rsa_default, ecdsa_default @@ -857,13 +872,13 @@ int main(int argc, char **argv) { /* We should need to skip valid non-default schemes to choose the defaults */ const struct s2n_signature_scheme *local_schemes[] = { - ecdsa_not_default_tls12, + &s2n_ecdsa_sha224, rsa_default, ecdsa_default, - ecdsa_not_default_tls13 + ecdsa_not_default }; const struct s2n_signature_scheme *peer_schemes[] = { - ecdsa_not_default_tls12, + &s2n_ecdsa_sha224, /* TLS1.3 does not support the TLS1.2 defaults */ rsa_default, ecdsa_default @@ -882,7 +897,7 @@ int main(int argc, char **argv) peer_schemes, s2n_array_len(peer_schemes))); EXPECT_OK(s2n_signature_algorithm_select(conn)); - EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, ecdsa_not_default_tls13); + EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, ecdsa_not_default); }; }; }; @@ -1041,44 +1056,6 @@ int main(int argc, char **argv) }; }; - /* Test: choose correct signature for duplicate iana values. - * Some signature schemes have the same iana, but are different for - * different protocol versions. */ - { - const struct s2n_signature_scheme *const dup_test_signature_schemes[] = { - &s2n_ecdsa_secp384r1_sha384, - &s2n_ecdsa_sha384, - }; - - const struct s2n_signature_preferences dup_test_preferences = { - .count = 2, - .signature_schemes = dup_test_signature_schemes, - }; - - DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - - const struct s2n_security_policy *security_policy = NULL; - EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); - EXPECT_NOT_NULL(security_policy); - struct s2n_security_policy test_security_policy = *security_policy; - test_security_policy.signature_preferences = &dup_test_preferences; - conn->security_policy_override = &test_security_policy; - - DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); - EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); - - conn->actual_protocol_version = S2N_TLS13; - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&input, s2n_ecdsa_sha384.iana_value)); - EXPECT_OK(s2n_signature_algorithm_recv(conn, &input)); - EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, &s2n_ecdsa_secp384r1_sha384); - - conn->actual_protocol_version = S2N_TLS12; - EXPECT_SUCCESS(s2n_stuffer_write_uint16(&input, s2n_ecdsa_sha384.iana_value)); - EXPECT_OK(s2n_signature_algorithm_recv(conn, &input)); - EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme, &s2n_ecdsa_sha384); - }; - /* Test: send and receive default signature preferences */ for (size_t i = S2N_TLS10; i < S2N_TLS13; i++) { DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), @@ -1396,6 +1373,107 @@ int main(int argc, char **argv) }; }; + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Self-Talk test: In TLS1.3, the ECDSA signature scheme curve must match + * the ECDSA certificate curve. + * + * But: + * Signature scheme curves do NOT have to match certificate curves in TLS1.2. + * Signature scheme curves do NOT have to match the ECDHE curve. + * Signature scheme hashes do NOT have to match PRF hashes. + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + + /* Certificate uses p521 */ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_p521_chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_p521_chain, + S2N_ECDSA_P512_CERT_CHAIN, S2N_ECDSA_P512_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_p521_chain)); + + /* Cipher should use SHA256 for PRF */ + struct s2n_cipher_suite *cipher_suite_tls13 = &s2n_tls13_aes_128_gcm_sha256; + struct s2n_cipher_suite *cipher_suite_tls12 = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + struct s2n_cipher_preferences cipher_prefs = { + .count = 1, + .suites = NULL + }; + /* TLS1.2 prefers SHA224 for signatures. + * TLS1.3 has to use SHA512 to match the certificate. + * Include another valid TLS1.3 option (SHA256) to verify SHA512 is still chosen. + */ + const struct s2n_signature_scheme *sig_schemes[] = { + &s2n_ecdsa_sha224, + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha512 + }; + struct s2n_signature_preferences sig_prefs = { + .count = s2n_array_len(sig_schemes), + .signature_schemes = sig_schemes + }; + /* Key exchange prefers SHA384 */ + const struct s2n_ecc_named_curve *curves[] = { + &s2n_ecc_curve_secp384r1, + &s2n_ecc_curve_secp521r1 + }; + struct s2n_ecc_preferences ecc_prefs = { + .count = s2n_array_len(curves), + .ecc_curves = curves + }; + struct s2n_security_policy policy = security_policy_20230317; + policy.cipher_preferences = &cipher_prefs; + policy.signature_preferences = &sig_prefs; + policy.ecc_preferences = &ecc_prefs; + config->security_policy = &policy; + + for (uint8_t version = S2N_TLS12; version <= S2N_TLS13; version++) { + if (version >= S2N_TLS13) { + cipher_prefs.suites = &cipher_suite_tls13; + } else { + cipher_prefs.suites = &cipher_suite_tls12; + } + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(client->actual_protocol_version, version); + EXPECT_EQUAL(server->actual_protocol_version, version); + + if (version >= S2N_TLS13) { + /* TLS1.3 sig scheme does have to match certificate: 512 */ + EXPECT_EQUAL(server->handshake_params.server_cert_sig_scheme, &s2n_ecdsa_sha512); + } else { + /* TLS1.2 sig scheme does not have to match certificate: 224 */ + EXPECT_NOT_EQUAL(server->handshake_params.server_cert_sig_scheme, &s2n_ecdsa_sha512); + EXPECT_EQUAL(server->handshake_params.server_cert_sig_scheme, &s2n_ecdsa_sha224); + } + + /* PRF does not have to match certificate or sig scheme: 256 */ + EXPECT_NOT_EQUAL(server->secure->cipher_suite->prf_alg, S2N_HMAC_SHA512); + EXPECT_EQUAL(server->secure->cipher_suite->prf_alg, S2N_HMAC_SHA256); + + /* KEX does not have to match certificate or sig scheme or PRF: 384 */ + EXPECT_NOT_EQUAL(server->kex_params.server_ecc_evp_params.negotiated_curve, + &s2n_ecc_curve_secp521r1); + EXPECT_EQUAL(server->kex_params.server_ecc_evp_params.negotiated_curve, + &s2n_ecc_curve_secp384r1); + } + } + END_TEST(); return 0; diff --git a/tests/unit/s2n_signature_scheme_test.c b/tests/unit/s2n_signature_scheme_test.c index 83ff401e175..ec4c052baab 100644 --- a/tests/unit/s2n_signature_scheme_test.c +++ b/tests/unit/s2n_signature_scheme_test.c @@ -35,11 +35,21 @@ int main(int argc, char **argv) EXPECT_NOT_EQUAL(sig_scheme->libcrypto_nid, 0); if (sig_scheme->sig_alg == S2N_SIGNATURE_ECDSA - && sig_scheme->minimum_protocol_version == S2N_TLS13) { + && sig_scheme->maximum_protocol_version != S2N_TLS12) { EXPECT_NOT_NULL(sig_scheme->signature_curve); } else { EXPECT_NULL(sig_scheme->signature_curve); } + + /* No duplicate signature schemes are allowed */ + for (size_t dup_i = 0; dup_i < sig_prefs->count; dup_i++) { + if (dup_i == sig_i) { + continue; + } + const struct s2n_signature_scheme *const potential_duplicate = + sig_prefs->signature_schemes[dup_i]; + EXPECT_NOT_EQUAL(sig_scheme->iana_value, potential_duplicate->iana_value); + } } policy_i++; } diff --git a/tests/unit/s2n_tls13_cert_verify_test.c b/tests/unit/s2n_tls13_cert_verify_test.c index 27b9cb9c4de..07a5b3add57 100644 --- a/tests/unit/s2n_tls13_cert_verify_test.c +++ b/tests/unit/s2n_tls13_cert_verify_test.c @@ -34,7 +34,7 @@ struct s2n_tls13_cert_verify_test { }; const struct s2n_tls13_cert_verify_test test_cases[] = { - { .cert_file = S2N_ECDSA_P384_PKCS1_CERT_CHAIN, .key_file = S2N_ECDSA_P384_PKCS1_KEY, .sig_scheme = &s2n_ecdsa_secp256r1_sha256 }, + { .cert_file = S2N_ECDSA_P384_PKCS1_CERT_CHAIN, .key_file = S2N_ECDSA_P384_PKCS1_KEY, .sig_scheme = &s2n_ecdsa_sha256 }, #if RSA_PSS_CERTS_SUPPORTED { .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, .sig_scheme = &s2n_rsa_pss_pss_sha256 }, #endif diff --git a/tests/unit/s2n_tls13_new_session_ticket_test.c b/tests/unit/s2n_tls13_new_session_ticket_test.c index 627299e7214..2e7dafb48cd 100644 --- a/tests/unit/s2n_tls13_new_session_ticket_test.c +++ b/tests/unit/s2n_tls13_new_session_ticket_test.c @@ -46,29 +46,6 @@ static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, st return S2N_SUCCESS; } -static int s2n_setup_test_ticket_key(struct s2n_config *config) -{ - POSIX_ENSURE_REF(config); - - /** - *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 - *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 - *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) - **/ - S2N_BLOB_FROM_HEX(ticket_key, - "077709362c2e32df0ddc3f0dc47bba63" - "90b6c73bb50f9c3122ec844ad7c2b3e5"); - - /* Set up encryption key */ - uint64_t current_time = 0; - uint8_t ticket_key_name[S2N_TICKET_KEY_NAME_LEN] = "2016.07.26.15\0"; - EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); - EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), - ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); - - return S2N_SUCCESS; -} - int main(int argc, char **argv) { BEGIN_TEST(); @@ -93,9 +70,8 @@ int main(int argc, char **argv) struct s2n_cert_chain_and_key *chain_and_key = NULL; EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); - EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); - EXPECT_SUCCESS(s2n_setup_test_ticket_key(server_config)); + EXPECT_OK(s2n_resumption_test_ticket_key_setup(server_config)); EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(client_config, s2n_test_session_ticket_cb, NULL)); diff --git a/tls/extensions/s2n_cert_authorities.c b/tls/extensions/s2n_cert_authorities.c new file mode 100644 index 00000000000..1043dfe89ee --- /dev/null +++ b/tls/extensions/s2n_cert_authorities.c @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include "tls/extensions/s2n_cert_authorities.h" + +#include "utils/s2n_safety.h" + +int s2n_cert_authorities_send(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_EQ(conn->mode, S2N_SERVER); + struct s2n_blob *cert_authorities = &conn->config->cert_authorities; + POSIX_GUARD(s2n_stuffer_write_uint16(out, cert_authorities->size)); + POSIX_GUARD(s2n_stuffer_write(out, cert_authorities)); + return S2N_SUCCESS; +} + +static bool s2n_cert_authorities_should_send(struct s2n_connection *conn) +{ + return conn && conn->config && conn->config->cert_authorities.size > 0; +} + +const s2n_extension_type s2n_cert_authorities_extension = { + .iana_value = TLS_EXTENSION_CERT_AUTHORITIES, + .minimum_version = S2N_TLS13, + .is_response = false, + .send = s2n_cert_authorities_send, + .should_send = s2n_cert_authorities_should_send, + /* s2n-tls supports sending the extension, but does not support parsing it. + * If received, the extension is ignored. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.4 + *= type=exception + *= reason=Extension ignored when received - No customer use case + *# The "certificate_authorities" extension is used to indicate the + *# certificate authorities (CAs) which an endpoint supports and which + *# SHOULD be used by the receiving endpoint to guide certificate + *# selection. + */ + .recv = s2n_extension_recv_noop, + .if_missing = s2n_extension_noop_if_missing, +}; diff --git a/tls/extensions/s2n_cert_authorities.h b/tls/extensions/s2n_cert_authorities.h new file mode 100644 index 00000000000..6821b1ad99a --- /dev/null +++ b/tls/extensions/s2n_cert_authorities.h @@ -0,0 +1,24 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#pragma once + +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_extension_type.h" +#include "tls/s2n_connection.h" + +extern const s2n_extension_type s2n_cert_authorities_extension; + +int s2n_cert_authorities_send(struct s2n_connection *conn, struct s2n_stuffer *out); diff --git a/tls/extensions/s2n_extension_list.h b/tls/extensions/s2n_extension_list.h index 411c7e4d7cc..ddc1a6be430 100644 --- a/tls/extensions/s2n_extension_list.h +++ b/tls/extensions/s2n_extension_list.h @@ -21,8 +21,8 @@ #define S2N_PARSED_EXTENSIONS_COUNT S2N_SUPPORTED_EXTENSIONS_COUNT typedef struct { - uint16_t extension_type; struct s2n_blob extension; + uint16_t extension_type; uint16_t wire_index; unsigned processed : 1; } s2n_parsed_extension; diff --git a/tls/extensions/s2n_extension_type.h b/tls/extensions/s2n_extension_type.h index eec792dd8c4..b1c1ef9f940 100644 --- a/tls/extensions/s2n_extension_type.h +++ b/tls/extensions/s2n_extension_type.h @@ -62,12 +62,13 @@ static const uint16_t s2n_supported_extensions[] = { TLS_EXTENSION_SUPPORTED_VERSIONS, TLS_EXTENSION_KEY_SHARE, TLS_EXTENSION_COOKIE, - TLS_QUIC_TRANSPORT_PARAMETERS, + TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS, TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES, TLS_EXTENSION_PRE_SHARED_KEY, TLS_EXTENSION_EARLY_DATA, TLS_EXTENSION_EMS, TLS_EXTENSION_NPN, + TLS_EXTENSION_CERT_AUTHORITIES, }; typedef char s2n_extension_bitfield[S2N_SUPPORTED_EXTENSIONS_BITFIELD_LEN]; diff --git a/tls/extensions/s2n_extension_type_lists.c b/tls/extensions/s2n_extension_type_lists.c index 3e2a607fd35..849d626acee 100644 --- a/tls/extensions/s2n_extension_type_lists.c +++ b/tls/extensions/s2n_extension_type_lists.c @@ -16,6 +16,7 @@ #include "tls/extensions/s2n_extension_type_lists.h" #include "api/s2n.h" +#include "tls/extensions/s2n_cert_authorities.h" #include "tls/extensions/s2n_cert_status.h" #include "tls/extensions/s2n_cert_status_response.h" #include "tls/extensions/s2n_client_alpn.h" @@ -131,6 +132,17 @@ static const s2n_extension_type *const encrypted_extensions[] = { static const s2n_extension_type *const cert_req_extensions[] = { &s2n_server_signature_algorithms_extension, &s2n_server_cert_status_request_extension, + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.4 + *= type=exception + *= reason=Currently only supported for servers -- no client use case + *# The client MAY send the "certificate_authorities" extension in the + *# ClientHello message. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.4 + *# The server MAY send it in the CertificateRequest message. + */ + &s2n_cert_authorities_extension, }; static const s2n_extension_type *const certificate_extensions[] = { diff --git a/tls/extensions/s2n_quic_transport_params.c b/tls/extensions/s2n_quic_transport_params.c index c4297df0d64..2de39f7725e 100644 --- a/tls/extensions/s2n_quic_transport_params.c +++ b/tls/extensions/s2n_quic_transport_params.c @@ -67,7 +67,7 @@ static int s2n_quic_transport_params_recv(struct s2n_connection *conn, struct s2 } const s2n_extension_type s2n_quic_transport_parameters_extension = { - .iana_value = TLS_QUIC_TRANSPORT_PARAMETERS, + .iana_value = TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS, .minimum_version = S2N_TLS13, .is_response = false, .send = s2n_quic_transport_params_send, diff --git a/tls/s2n_alerts.c b/tls/s2n_alerts.c index 88752b16d25..175c26c0e2f 100644 --- a/tls/s2n_alerts.c +++ b/tls/s2n_alerts.c @@ -43,6 +43,7 @@ static S2N_RESULT s2n_translate_protocol_error_to_alert(int error_code, uint8_t switch (error_code) { S2N_ALERT_CASE(S2N_ERR_MISSING_EXTENSION, S2N_TLS_ALERT_MISSING_EXTENSION); S2N_ALERT_CASE(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, S2N_TLS_ALERT_HANDSHAKE_FAILURE); + S2N_ALERT_CASE(S2N_ERR_MISSING_CLIENT_CERT, S2N_TLS_ALERT_CERTIFICATE_REQUIRED); /* TODO: The ERR_BAD_MESSAGE -> ALERT_UNEXPECTED_MESSAGE mapping * isn't always correct. Sometimes s2n-tls uses ERR_BAD_MESSAGE @@ -52,6 +53,7 @@ static S2N_RESULT s2n_translate_protocol_error_to_alert(int error_code, uint8_t */ S2N_ALERT_CASE(S2N_ERR_BAD_MESSAGE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); S2N_ALERT_CASE(S2N_ERR_UNEXPECTED_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + S2N_ALERT_CASE(S2N_ERR_MISSING_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); /* For errors involving secure renegotiation: *= https://tools.ietf.org/rfc/rfc5746#3.4 diff --git a/tls/s2n_auth_selection.c b/tls/s2n_auth_selection.c index 3d56a022104..e14c587e544 100644 --- a/tls/s2n_auth_selection.c +++ b/tls/s2n_auth_selection.c @@ -117,8 +117,8 @@ static int s2n_certs_exist_for_sig_scheme(struct s2n_connection *conn, const str struct s2n_cert_chain_and_key *cert = s2n_get_compatible_cert_chain_and_key(conn, cert_type); POSIX_ENSURE_REF(cert); - /* For sig_algs that include a curve, the group must also match. */ - if (sig_scheme->signature_curve != NULL) { + /* For TLS1.3 sig_algs that include a curve, the group must also match. */ + if (sig_scheme->signature_curve && conn->actual_protocol_version >= S2N_TLS13) { POSIX_ENSURE_REF(cert->private_key); POSIX_ENSURE_REF(cert->cert_chain); POSIX_ENSURE_REF(cert->cert_chain->head); diff --git a/tls/s2n_cipher_preferences.c b/tls/s2n_cipher_preferences.c index 3552c72aaa8..d77f863c443 100644 --- a/tls/s2n_cipher_preferences.c +++ b/tls/s2n_cipher_preferences.c @@ -302,6 +302,31 @@ const struct s2n_cipher_preferences cipher_preferences_20230317 = { .allow_chacha20_boosting = false, }; +/* + * No TLS1.3 support. + * FIPS compliant. + * No DHE (would require extra setup with s2n_config_add_dhparams) + */ +struct s2n_cipher_suite *cipher_suites_20240331[] = { + /* TLS1.2 with ECDSA */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, + + /* TLS1.2 with RSA */ + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, +}; + +const struct s2n_cipher_preferences cipher_preferences_20240331 = { + .count = s2n_array_len(cipher_suites_20240331), + .suites = cipher_suites_20240331, + .allow_chacha20_boosting = false, +}; + /* Same as 20160411, but with ChaCha20 added as 1st in Preference List */ struct s2n_cipher_suite *cipher_suites_20190122[] = { &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, diff --git a/tls/s2n_cipher_preferences.h b/tls/s2n_cipher_preferences.h index dc68e1fbb8c..4c27b8fc97d 100644 --- a/tls/s2n_cipher_preferences.h +++ b/tls/s2n_cipher_preferences.h @@ -28,6 +28,7 @@ struct s2n_cipher_preferences { }; extern const struct s2n_cipher_preferences cipher_preferences_20230317; +extern const struct s2n_cipher_preferences cipher_preferences_20240331; extern const struct s2n_cipher_preferences cipher_preferences_20140601; extern const struct s2n_cipher_preferences cipher_preferences_20141001; extern const struct s2n_cipher_preferences cipher_preferences_20150202; diff --git a/tls/s2n_config.c b/tls/s2n_config.c index aea8689a217..b595a52c3e3 100644 --- a/tls/s2n_config.c +++ b/tls/s2n_config.c @@ -124,6 +124,7 @@ static int s2n_config_cleanup(struct s2n_config *config) POSIX_GUARD(s2n_config_free_cert_chain_and_key(config)); POSIX_GUARD(s2n_config_free_dhparams(config)); POSIX_GUARD(s2n_free(&config->application_protocols)); + POSIX_GUARD(s2n_free(&config->cert_authorities)); POSIX_GUARD_RESULT(s2n_map_free(config->domain_name_to_cert_map)); POSIX_CHECKED_MEMSET(config, 0, sizeof(struct s2n_config)); @@ -1127,6 +1128,10 @@ int s2n_config_set_verify_after_sign(struct s2n_config *config, s2n_verify_after int s2n_config_set_renegotiate_request_cb(struct s2n_config *config, s2n_renegotiate_request_cb cb, void *ctx) { POSIX_ENSURE_REF(config); + + /* This feature cannot be used with serialization currently */ + POSIX_ENSURE(config->serialized_connection_version == S2N_SERIALIZED_CONN_NONE, S2N_ERR_INVALID_STATE); + config->renegotiate_request_cb = cb; config->renegotiate_request_ctx = ctx; return S2N_SUCCESS; @@ -1219,3 +1224,17 @@ int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, return S2N_SUCCESS; } + +int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serialization_version version) +{ + POSIX_ENSURE_REF(config); + + /* This feature cannot be used with renegotiation currently */ + POSIX_ENSURE(config->renegotiate_request_cb == NULL, S2N_ERR_INVALID_STATE); + + /* Currently there is only one format version supported */ + POSIX_ENSURE_EQ(version, S2N_SERIALIZED_CONN_V1); + config->serialized_connection_version = version; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_config.h b/tls/s2n_config.h index 10da5ce8f1a..714c8a2328b 100644 --- a/tls/s2n_config.h +++ b/tls/s2n_config.h @@ -208,6 +208,26 @@ struct s2n_config { void *renegotiate_request_ctx; s2n_renegotiate_request_cb renegotiate_request_cb; + + /* This version is meant as a safeguard against future TLS features which might affect the connection + * serialization feature. + * + * For example, suppose that a new TLS parameter is released which affects how data is sent + * post-handshake. This parameter must be available in both the s2n-tls version that serializes the + * connection, as well as the version that deserializes the connection. If not, the serializer + * may negotiate this feature with its peer, which would cause an older deserializer to run into errors + * sending data to the peer. + * + * This kind of version-mismatch can happen during deployments and rollbacks, and therefore we require + * the user to tell us which serialized version they support pre-handshake. + * We will not negotiate a new feature until the user requests the serialized connection + * version the feature is tied to (i.e. the request indicates they have finished deploying + * the new feature to their entire fleet.) + */ + s2n_serialization_version serialized_connection_version; + + /* List of certificate authorities supported */ + struct s2n_blob cert_authorities; }; S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config); diff --git a/tls/s2n_connection.c b/tls/s2n_connection.c index 1a555bdc054..807fc85ba52 100644 --- a/tls/s2n_connection.c +++ b/tls/s2n_connection.c @@ -102,7 +102,7 @@ struct s2n_connection *s2n_connection_new(s2n_mode mode) PTR_GUARD_POSIX(s2n_blob_init(&blob, conn->header_in_data, S2N_TLS_RECORD_HEADER_LENGTH)); PTR_GUARD_POSIX(s2n_stuffer_init(&conn->header_in, &blob)); PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->out, 0)); - PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->in, 0)); + PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); PTR_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->handshake.io, 0)); PTR_GUARD_RESULT(s2n_timer_start(conn->config, &conn->write_timer)); @@ -262,6 +262,7 @@ int s2n_connection_free(struct s2n_connection *conn) POSIX_GUARD(s2n_free(&conn->peer_quic_transport_parameters)); POSIX_GUARD(s2n_free(&conn->server_early_data_context)); POSIX_GUARD(s2n_free(&conn->tls13_ticket_fields.session_secret)); + POSIX_GUARD(s2n_stuffer_free(&conn->buffer_in)); POSIX_GUARD(s2n_stuffer_free(&conn->in)); POSIX_GUARD(s2n_stuffer_free(&conn->out)); POSIX_GUARD(s2n_stuffer_free(&conn->handshake.io)); @@ -407,7 +408,9 @@ int s2n_connection_release_buffers(struct s2n_connection *conn) POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); - POSIX_GUARD(s2n_stuffer_resize(&conn->in, 0)); + if (s2n_stuffer_is_consumed(&conn->buffer_in)) { + POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); + } POSIX_ENSURE(s2n_stuffer_is_consumed(&conn->post_handshake.in), S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); @@ -464,7 +467,7 @@ int s2n_connection_wipe(struct s2n_connection *conn) struct s2n_stuffer client_ticket_to_decrypt = { 0 }; struct s2n_stuffer handshake_io = { 0 }; struct s2n_stuffer header_in = { 0 }; - struct s2n_stuffer in = { 0 }; + struct s2n_stuffer buffer_in = { 0 }; struct s2n_stuffer out = { 0 }; /* Some required structures might have been freed to conserve memory between handshakes. @@ -501,11 +504,12 @@ int s2n_connection_wipe(struct s2n_connection *conn) POSIX_GUARD(s2n_stuffer_wipe(&conn->post_handshake.in)); POSIX_GUARD(s2n_blob_zero(&conn->client_hello.raw_message)); POSIX_GUARD(s2n_stuffer_wipe(&conn->header_in)); - POSIX_GUARD(s2n_stuffer_wipe(&conn->in)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->buffer_in)); POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); /* Free stuffers we plan to just recreate */ POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in)); + POSIX_GUARD(s2n_stuffer_free(&conn->in)); POSIX_GUARD_RESULT(s2n_psk_parameters_wipe(&conn->psk_params)); @@ -526,7 +530,7 @@ int s2n_connection_wipe(struct s2n_connection *conn) /* Truncate the message buffers to save memory, we will dynamically resize it as needed */ POSIX_GUARD(s2n_free(&conn->client_hello.raw_message)); - POSIX_GUARD(s2n_stuffer_resize(&conn->in, 0)); + POSIX_GUARD(s2n_stuffer_resize(&conn->buffer_in, 0)); POSIX_GUARD(s2n_stuffer_resize(&conn->out, 0)); /* Remove context associated with connection */ @@ -545,7 +549,7 @@ int s2n_connection_wipe(struct s2n_connection *conn) POSIX_CHECKED_MEMCPY(&client_ticket_to_decrypt, &conn->client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); POSIX_CHECKED_MEMCPY(&handshake_io, &conn->handshake.io, sizeof(struct s2n_stuffer)); POSIX_CHECKED_MEMCPY(&header_in, &conn->header_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&in, &conn->in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&buffer_in, &conn->buffer_in, sizeof(struct s2n_stuffer)); POSIX_CHECKED_MEMCPY(&out, &conn->out, sizeof(struct s2n_stuffer)); #ifdef S2N_DIAGNOSTICS_POP_SUPPORTED #pragma GCC diagnostic pop @@ -557,9 +561,14 @@ int s2n_connection_wipe(struct s2n_connection *conn) POSIX_CHECKED_MEMCPY(&conn->client_ticket_to_decrypt, &client_ticket_to_decrypt, sizeof(struct s2n_stuffer)); POSIX_CHECKED_MEMCPY(&conn->handshake.io, &handshake_io, sizeof(struct s2n_stuffer)); POSIX_CHECKED_MEMCPY(&conn->header_in, &header_in, sizeof(struct s2n_stuffer)); - POSIX_CHECKED_MEMCPY(&conn->in, &in, sizeof(struct s2n_stuffer)); + POSIX_CHECKED_MEMCPY(&conn->buffer_in, &buffer_in, sizeof(struct s2n_stuffer)); POSIX_CHECKED_MEMCPY(&conn->out, &out, sizeof(struct s2n_stuffer)); + /* conn->in will eventually point to part of conn->buffer_in, but we initialize + * it as growable and allocated to support legacy tests. + */ + POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->in, 0)); + conn->handshake.hashes = handshake_hashes; conn->prf_space = prf_workspace; conn->initial = initial; @@ -1215,6 +1224,9 @@ S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection **c return S2N_RESULT_OK; } + /* Ensure that conn->in doesn't contain any leftover invalid or unauthenticated data. */ + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&(*conn)->in)); + int error_code = s2n_errno; int error_type = s2n_error_get_type(error_code); @@ -1628,13 +1640,13 @@ S2N_RESULT s2n_connection_dynamic_free_in_buffer(struct s2n_connection *conn) { RESULT_ENSURE_REF(conn); - /* free the `in` buffer if we're in dynamic mode and it's completely flushed */ - if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->in)) { + /* free `buffer_in` if we're in dynamic mode and it's completely flushed */ + if (conn->dynamic_buffers && s2n_stuffer_is_consumed(&conn->buffer_in)) { /* when copying the buffer into the application, we use `s2n_stuffer_erase_and_read`, which already zeroes the memory */ - RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->in)); + RESULT_GUARD_POSIX(s2n_stuffer_free_without_wipe(&conn->buffer_in)); /* reset the stuffer to its initial state */ - RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->in, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); } return S2N_RESULT_OK; @@ -1726,3 +1738,12 @@ int s2n_connection_get_key_update_counts(struct s2n_connection *conn, *recv_key_updates = conn->recv_key_updated; return S2N_SUCCESS; } + +int s2n_connection_set_recv_buffering(struct s2n_connection *conn, bool enabled) +{ + POSIX_ENSURE_REF(conn); + /* QUIC support is not currently compatible with recv_buffering */ + POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_INVALID_STATE); + conn->recv_buffering = enabled; + return S2N_SUCCESS; +} diff --git a/tls/s2n_connection.h b/tls/s2n_connection.h index 44f3e892345..58c4757cac8 100644 --- a/tls/s2n_connection.h +++ b/tls/s2n_connection.h @@ -133,6 +133,18 @@ struct s2n_connection { /* Indicates whether the connection should request OCSP stapling from the peer */ unsigned request_ocsp_status : 1; + /* Indicates that the connection was created from deserialization + * and therefore knowledge of the original handshake is limited. */ + unsigned deserialized_conn : 1; + + /* Indicates s2n_recv should reduce read calls by attempting to buffer more + * data than is required for a single record. + * + * This is more efficient, but will break applications that expect exact reads, + * for example any custom IO that behaves like MSG_WAITALL. + */ + unsigned recv_buffering : 1; + /* The configuration (cert, key .. etc ) */ struct s2n_config *config; @@ -240,6 +252,7 @@ struct s2n_connection { */ uint8_t header_in_data[S2N_TLS_RECORD_HEADER_LENGTH]; struct s2n_stuffer header_in; + struct s2n_stuffer buffer_in; struct s2n_stuffer in; struct s2n_stuffer out; enum { diff --git a/tls/s2n_connection_serialize.c b/tls/s2n_connection_serialize.c new file mode 100644 index 00000000000..1f959ed502c --- /dev/null +++ b/tls/s2n_connection_serialize.c @@ -0,0 +1,285 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "tls/s2n_connection_serialize.h" + +#include "tls/s2n_connection.h" +#include "tls/s2n_tls13_key_schedule.h" + +int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(length); + + POSIX_ENSURE(conn->config->serialized_connection_version != S2N_SERIALIZED_CONN_NONE, + S2N_ERR_INVALID_STATE); + + if (conn->actual_protocol_version >= S2N_TLS13) { + uint8_t secret_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + *length = S2N_SERIALIZED_CONN_FIXED_SIZE + (secret_size * 3); + } else { + *length = S2N_SERIALIZED_CONN_TLS12_SIZE; + } + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_connection_serialize_tls13_secrets(struct s2n_connection *conn, struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + + uint8_t secret_size = 0; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->secrets.version.tls13.client_app_secret, + secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->secrets.version.tls13.server_app_secret, + secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->secrets.version.tls13.resumption_master_secret, + secret_size)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_serialize_secrets(struct s2n_connection *conn, struct s2n_stuffer *output) +{ + RESULT_ENSURE_REF(conn); + + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->secrets.version.tls12.master_secret, + S2N_TLS_SECRET_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->handshake_params.client_random, + S2N_TLS_RANDOM_DATA_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(output, conn->handshake_params.server_random, + S2N_TLS_RANDOM_DATA_LEN)); + return S2N_RESULT_OK; +} + +int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(conn->secure->cipher_suite); + POSIX_ENSURE_REF(conn->config); + POSIX_ENSURE_REF(buffer); + + POSIX_ENSURE(conn->config->serialized_connection_version != S2N_SERIALIZED_CONN_NONE, + S2N_ERR_INVALID_STATE); + + /* This method must be called after negotiation */ + POSIX_ENSURE(s2n_handshake_is_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); + + /* Best effort check for pending input or output data. + * This method should not be called until the application has stopped sending and receiving. + * Saving partial read or partial write state would complicate this problem. + */ + POSIX_ENSURE(s2n_stuffer_data_available(&conn->header_in) == 0, S2N_ERR_INVALID_STATE); + POSIX_ENSURE(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_INVALID_STATE); + POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_INVALID_STATE); + + uint32_t context_length = 0; + POSIX_GUARD(s2n_connection_serialization_length(conn, &context_length)); + POSIX_ENSURE(buffer_length >= context_length, S2N_ERR_INSUFFICIENT_MEM_SIZE); + + struct s2n_blob context_blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&context_blob, buffer, buffer_length)); + struct s2n_stuffer output = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&output, &context_blob)); + + POSIX_GUARD(s2n_stuffer_write_uint64(&output, S2N_SERIALIZED_CONN_V1)); + + POSIX_GUARD(s2n_stuffer_write_uint8(&output, conn->actual_protocol_version / 10)); + POSIX_GUARD(s2n_stuffer_write_uint8(&output, conn->actual_protocol_version % 10)); + POSIX_GUARD(s2n_stuffer_write_bytes(&output, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + + POSIX_GUARD(s2n_stuffer_write_bytes(&output, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + POSIX_GUARD(s2n_stuffer_write_bytes(&output, conn->secure->server_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + + POSIX_GUARD(s2n_stuffer_write_uint16(&output, conn->max_outgoing_fragment_length)); + + if (conn->actual_protocol_version >= S2N_TLS13) { + POSIX_GUARD_RESULT(s2n_connection_serialize_tls13_secrets(conn, &output)); + } else { + POSIX_GUARD_RESULT(s2n_connection_serialize_secrets(conn, &output)); + } + + return S2N_SUCCESS; +} + +struct s2n_connection_deserialize { + uint8_t protocol_version; + struct s2n_cipher_suite *cipher_suite; + uint8_t client_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN]; + uint8_t server_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN]; + uint16_t max_fragment_len; + union { + struct { + uint8_t master_secret[S2N_TLS_SECRET_LEN]; + uint8_t client_random[S2N_TLS_RANDOM_DATA_LEN]; + uint8_t server_random[S2N_TLS_RANDOM_DATA_LEN]; + } tls12; + struct { + uint8_t secret_size; + uint8_t client_application_secret[S2N_TLS_SECRET_LEN]; + uint8_t server_application_secret[S2N_TLS_SECRET_LEN]; + uint8_t resumption_master_secret[S2N_TLS_SECRET_LEN]; + } tls13; + } version; +}; + +static S2N_RESULT s2n_connection_deserialize_tls13_secrets(struct s2n_stuffer *input, + struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(input); + RESULT_ENSURE_REF(parsed_values); + + RESULT_GUARD_POSIX(s2n_hmac_digest_size(parsed_values->cipher_suite->prf_alg, + &parsed_values->version.tls13.secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls13.client_application_secret, + parsed_values->version.tls13.secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls13.server_application_secret, + parsed_values->version.tls13.secret_size)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls13.resumption_master_secret, + parsed_values->version.tls13.secret_size)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_deserialize_secrets(struct s2n_stuffer *input, + struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(input); + RESULT_ENSURE_REF(parsed_values); + + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls12.client_random, S2N_TLS_RANDOM_DATA_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(input, parsed_values->version.tls12.server_random, S2N_TLS_RANDOM_DATA_LEN)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_connection_deserialize_parse(uint8_t *buffer, uint32_t buffer_length, + struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(parsed_values); + + struct s2n_blob context_blob = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&context_blob, buffer, buffer_length)); + struct s2n_stuffer input = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&input, &context_blob)); + + uint64_t serialized_version = 0; + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(&input, &serialized_version)); + /* No other version is supported currently */ + RESULT_ENSURE_EQ(serialized_version, S2N_SERIALIZED_CONN_V1); + + uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(&input, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + parsed_values->protocol_version = (protocol_version[0] * 10) + protocol_version[1]; + + uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(&input, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + RESULT_GUARD(s2n_cipher_suite_from_iana(cipher_suite, S2N_TLS_CIPHER_SUITE_LEN, &parsed_values->cipher_suite)); + + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(&input, parsed_values->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(&input, parsed_values->server_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + + RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(&input, &parsed_values->max_fragment_len)); + + if (parsed_values->protocol_version >= S2N_TLS13) { + RESULT_GUARD(s2n_connection_deserialize_tls13_secrets(&input, parsed_values)); + } else { + RESULT_GUARD(s2n_connection_deserialize_secrets(&input, parsed_values)); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_restore_tls13_secrets(struct s2n_connection *conn, struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(parsed_values); + + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.client_app_secret, + parsed_values->version.tls13.client_application_secret, parsed_values->version.tls13.secret_size); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.server_app_secret, + parsed_values->version.tls13.server_application_secret, parsed_values->version.tls13.secret_size); + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls13.resumption_master_secret, + parsed_values->version.tls13.resumption_master_secret, parsed_values->version.tls13.secret_size); + + RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, S2N_MASTER_SECRET, S2N_SERVER)); + RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, S2N_MASTER_SECRET, S2N_CLIENT)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_restore_secrets(struct s2n_connection *conn, struct s2n_connection_deserialize *parsed_values) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(parsed_values); + + RESULT_CHECKED_MEMCPY(conn->secrets.version.tls12.master_secret, parsed_values->version.tls12.master_secret, + S2N_TLS_SECRET_LEN); + RESULT_CHECKED_MEMCPY(conn->handshake_params.client_random, parsed_values->version.tls12.client_random, + S2N_TLS_RANDOM_DATA_LEN); + RESULT_CHECKED_MEMCPY(conn->handshake_params.server_random, parsed_values->version.tls12.server_random, + S2N_TLS_RANDOM_DATA_LEN); + RESULT_GUARD_POSIX(s2n_prf_key_expansion(conn)); + + return S2N_RESULT_OK; +} + +int s2n_connection_deserialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(conn->secure); + POSIX_ENSURE_REF(buffer); + + /* Read parsed values into a temporary struct so that the connection is unaltered if parsing fails */ + struct s2n_connection_deserialize parsed_values = { 0 }; + POSIX_ENSURE(s2n_result_is_ok(s2n_connection_deserialize_parse(buffer, buffer_length, &parsed_values)), + S2N_ERR_INVALID_SERIALIZED_CONNECTION); + + /* Rehydrate fields now that parsing has completed successfully */ + conn->actual_protocol_version = parsed_values.protocol_version; + conn->secure->cipher_suite = parsed_values.cipher_suite; + POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, parsed_values.max_fragment_len)); + + /* Mark the connection as having been deserialized */ + conn->deserialized_conn = true; + + /* Key expansion */ + if (parsed_values.protocol_version >= S2N_TLS13) { + POSIX_GUARD_RESULT(s2n_restore_tls13_secrets(conn, &parsed_values)); + } else { + POSIX_GUARD_RESULT(s2n_restore_secrets(conn, &parsed_values)); + } + + /* Wait until after key generation to restore sequence numbers since they get zeroed during + * key expansion */ + POSIX_CHECKED_MEMCPY(conn->secure->client_sequence_number, parsed_values.client_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + POSIX_CHECKED_MEMCPY(conn->secure->server_sequence_number, parsed_values.server_sequence_number, + S2N_TLS_SEQUENCE_NUM_LEN); + + conn->client = conn->secure; + conn->server = conn->secure; + + return S2N_SUCCESS; +} diff --git a/tls/s2n_connection_serialize.h b/tls/s2n_connection_serialize.h new file mode 100644 index 00000000000..754c8b39aa3 --- /dev/null +++ b/tls/s2n_connection_serialize.h @@ -0,0 +1,25 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "tls/s2n_connection.h" + +#pragma once + +#define S2N_SERIALIZED_CONN_FIXED_SIZE (8 + S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_CIPHER_SUITE_LEN \ + + S2N_TLS_SEQUENCE_NUM_LEN + S2N_TLS_SEQUENCE_NUM_LEN + 2) +#define S2N_SERIALIZED_CONN_TLS12_SIZE (S2N_SERIALIZED_CONN_FIXED_SIZE + S2N_TLS_SECRET_LEN \ + + S2N_TLS_RANDOM_DATA_LEN + S2N_TLS_RANDOM_DATA_LEN) diff --git a/tls/s2n_handshake_io.c b/tls/s2n_handshake_io.c index 514c671b281..53130d86aba 100644 --- a/tls/s2n_handshake_io.c +++ b/tls/s2n_handshake_io.c @@ -1105,7 +1105,7 @@ int s2n_conn_set_handshake_no_client_cert(struct s2n_connection *conn) { s2n_cert_auth_type client_cert_auth_type; POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - S2N_ERROR_IF(client_cert_auth_type != S2N_CERT_AUTH_OPTIONAL, S2N_ERR_BAD_MESSAGE); + POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_MISSING_CLIENT_CERT); POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NO_CLIENT_CERT)); @@ -1499,8 +1499,9 @@ static int s2n_handshake_read_io(struct s2n_connection *conn) s2n_cert_auth_type client_cert_auth_type; POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - /* If we're a Client, and received a ClientCertRequest message, and ClientAuth - * is set to optional, then switch the State Machine that we're using to expect the ClientCertRequest. */ + /* If client auth is optional, we initially assume it will not be requested. + * If we received a request, switch to a client auth handshake. + */ if (conn->mode == S2N_CLIENT && client_cert_auth_type != S2N_CERT_AUTH_REQUIRED && message_type == TLS_CERT_REQ) { @@ -1529,6 +1530,12 @@ static int s2n_handshake_read_io(struct s2n_connection *conn) continue; } + /* Check for missing Certificate Requests to surface a more specific error */ + if (EXPECTED_MESSAGE_TYPE(conn) == TLS_CERT_REQ) { + POSIX_ENSURE(message_type == TLS_CERT_REQ, + S2N_ERR_MISSING_CERT_REQUEST); + } + POSIX_ENSURE(record_type == EXPECTED_RECORD_TYPE(conn), S2N_ERR_BAD_MESSAGE); POSIX_ENSURE(message_type == EXPECTED_MESSAGE_TYPE(conn), S2N_ERR_BAD_MESSAGE); POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); @@ -1591,7 +1598,9 @@ static int s2n_handle_retry_state(struct s2n_connection *conn) bool s2n_handshake_is_complete(struct s2n_connection *conn) { - return conn && ACTIVE_STATE(conn).writer == 'B'; + /* A deserialized connection implies that the handshake is complete because + * connections cannot be serialized before completing the handshake. */ + return conn && (ACTIVE_STATE(conn).writer == 'B' || conn->deserialized_conn); } int s2n_negotiate_impl(struct s2n_connection *conn, s2n_blocked_status *blocked) diff --git a/tls/s2n_ktls.c b/tls/s2n_ktls.c index 15caac00337..901fb1ad11a 100644 --- a/tls/s2n_ktls.c +++ b/tls/s2n_ktls.c @@ -90,6 +90,16 @@ static S2N_RESULT s2n_ktls_validate(struct s2n_connection *conn, s2n_ktls_mode k bool may_renegotiate = may_receive_hello_request && config->renegotiate_request_cb; RESULT_ENSURE(!may_renegotiate, S2N_ERR_KTLS_RENEG); + /* Prevent kTLS from being enabled on connections that might be serialized. + * + * The socket takes over tracking sequence numbers when kTLS is enabled. + * We would need to call getsockopt to retrieve the current sequence numbers for + * serialization. This would complicate the serialization implementation so + * for now, do not support kTLS with serialization. + */ + RESULT_ENSURE(config->serialized_connection_version == S2N_SERIALIZED_CONN_NONE, + S2N_ERR_KTLS_UNSUPPORTED_CONN); + /* kTLS I/O functionality is managed by s2n-tls. kTLS cannot be enabled if the * application sets custom I/O (managed_send_io == false means application has * set custom I/O). @@ -98,13 +108,14 @@ static S2N_RESULT s2n_ktls_validate(struct s2n_connection *conn, s2n_ktls_mode k case S2N_KTLS_MODE_SEND: RESULT_ENSURE(conn->managed_send_io, S2N_ERR_KTLS_MANAGED_IO); /* The output stuffer should be empty before enabling kTLS. */ - RESULT_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + RESULT_ENSURE(s2n_stuffer_is_consumed(&conn->out), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); break; case S2N_KTLS_MODE_RECV: RESULT_ENSURE(conn->managed_recv_io, S2N_ERR_KTLS_MANAGED_IO); /* The input stuffers should be empty before enabling kTLS. */ - RESULT_ENSURE(s2n_stuffer_data_available(&conn->header_in) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); - RESULT_ENSURE(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + RESULT_ENSURE(s2n_stuffer_is_consumed(&conn->header_in), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + RESULT_ENSURE(s2n_stuffer_is_consumed(&conn->in), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + RESULT_ENSURE(s2n_stuffer_is_consumed(&conn->buffer_in), S2N_ERR_KTLS_UNSUPPORTED_CONN); break; default: RESULT_BAIL(S2N_ERR_SAFETY); diff --git a/tls/s2n_ktls_io.c b/tls/s2n_ktls_io.c index 34a1de6f02d..ad1d984c6e2 100644 --- a/tls/s2n_ktls_io.c +++ b/tls/s2n_ktls_io.c @@ -532,9 +532,9 @@ int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type) return S2N_SUCCESS; } - POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->in, S2N_DEFAULT_FRAGMENT_LENGTH)); + POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_DEFAULT_FRAGMENT_LENGTH)); - struct s2n_stuffer record_stuffer = conn->in; + struct s2n_stuffer record_stuffer = conn->buffer_in; size_t len = s2n_stuffer_space_remaining(&record_stuffer); uint8_t *buf = s2n_stuffer_raw_write(&record_stuffer, len); POSIX_ENSURE_REF(buf); @@ -549,6 +549,12 @@ int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type) buf, len, &blocked, &bytes_read); WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result)); - POSIX_GUARD(s2n_stuffer_skip_write(&conn->in, bytes_read)); + POSIX_GUARD(s2n_stuffer_skip_write(&conn->buffer_in, bytes_read)); + + /* We don't care about returning a full fragment because we don't need to decrypt. + * kTLS handled decryption already. + * So we can always set conn->in equal to the full buffer_in. + */ + POSIX_GUARD_RESULT(s2n_recv_in_init(conn, bytes_read, bytes_read)); return S2N_SUCCESS; } diff --git a/tls/s2n_quic_support.c b/tls/s2n_quic_support.c index ba146eacbc9..881aa313796 100644 --- a/tls/s2n_quic_support.c +++ b/tls/s2n_quic_support.c @@ -45,6 +45,8 @@ int s2n_connection_enable_quic(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); POSIX_GUARD_RESULT(s2n_connection_validate_tls13_support(conn)); + /* QUIC support is not currently compatible with recv_buffering */ + POSIX_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); conn->quic_enabled = true; return S2N_SUCCESS; } @@ -130,9 +132,13 @@ int s2n_recv_quic_post_handshake_message(struct s2n_connection *conn, s2n_blocke S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type) { RESULT_ENSURE_REF(conn); + /* The use of handshake.io here would complicate recv_buffering, and there's + * no real use case for recv_buffering when QUIC is handling the IO. + */ + RESULT_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ - RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->in, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); + RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); @@ -141,8 +147,14 @@ S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io)); RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); - RESULT_GUARD(s2n_read_in_bytes(conn, &conn->in, message_len)); + RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, message_len)); + /* Although we call s2n_read_in_bytes, recv_greedy is always disabled for quic. + * Therefore buffer_in will always contain exactly message_len bytes of data. + * So we don't need to handle the possibility of extra data in buffer_in. + */ + RESULT_ENSURE_EQ(s2n_stuffer_data_available(&conn->buffer_in), message_len); + RESULT_GUARD(s2n_recv_in_init(conn, message_len, message_len)); return S2N_RESULT_OK; } diff --git a/tls/s2n_record_read.c b/tls/s2n_record_read.c index 5281e1734f3..13323a41470 100644 --- a/tls/s2n_record_read.c +++ b/tls/s2n_record_read.c @@ -274,5 +274,16 @@ S2N_RESULT s2n_record_wipe(struct s2n_connection *conn) RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->header_in)); RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->in)); conn->in_status = ENCRYPTED; + + /* Release the memory in conn->in, which un-taints buffer_in */ + RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); + conn->buffer_in.tainted = false; + + /* Reclaim any memory in buffer_in if possible. + * We want to avoid an expensive shift / copy later if possible. + */ + if (s2n_stuffer_is_consumed(&conn->buffer_in)) { + RESULT_GUARD_POSIX(s2n_stuffer_rewrite(&conn->buffer_in)); + } return S2N_RESULT_OK; } diff --git a/tls/s2n_record_read_cbc.c b/tls/s2n_record_read_cbc.c index 08fd52e3277..346e6571f7c 100644 --- a/tls/s2n_record_read_cbc.c +++ b/tls/s2n_record_read_cbc.c @@ -103,7 +103,6 @@ int s2n_record_parse_cbc( /* Padding. This finalizes the provided HMAC. */ if (s2n_verify_cbc(conn, mac, &en) < 0) { - POSIX_GUARD(s2n_stuffer_wipe(&conn->in)); POSIX_BAIL(S2N_ERR_BAD_MESSAGE); } diff --git a/tls/s2n_record_read_stream.c b/tls/s2n_record_read_stream.c index f40621b2fe1..927ab00fe7a 100644 --- a/tls/s2n_record_read_stream.c +++ b/tls/s2n_record_read_stream.c @@ -76,7 +76,6 @@ int s2n_record_parse_stream( POSIX_GUARD(s2n_hmac_digest(mac, check_digest, mac_digest_size)); if (s2n_hmac_digest_verify(en.data + payload_length, check_digest, mac_digest_size) < 0) { - POSIX_GUARD(s2n_stuffer_wipe(&conn->in)); POSIX_BAIL(S2N_ERR_BAD_MESSAGE); } diff --git a/tls/s2n_record_write.c b/tls/s2n_record_write.c index f0455c8842d..6057c199b43 100644 --- a/tls/s2n_record_write.c +++ b/tls/s2n_record_write.c @@ -32,6 +32,11 @@ extern uint8_t s2n_unknown_protocol_version; +/* In TLS1.3 the record type is obfuscated as APPLICATION_DATA once the handshake begins to be encrypted. + * The real record type is encrypted and written in the final byte of the record. + * In TLS1.2 the record type is always cleartext. */ +#define RECORD_TYPE(is_tls13_record, content_type) (is_tls13_record ? TLS_APPLICATION_DATA : content_type) + /* How much overhead does the IV, MAC, TAG and padding bytes introduce ? */ static S2N_RESULT s2n_tls_record_overhead(struct s2n_connection *conn, uint16_t *out) { @@ -155,7 +160,7 @@ S2N_RESULT s2n_record_min_write_payload_size(struct s2n_connection *conn, uint16 return S2N_RESULT_OK; } -int s2n_record_write_protocol_version(struct s2n_connection *conn, struct s2n_stuffer *out) +int s2n_record_write_protocol_version(struct s2n_connection *conn, uint8_t record_type, struct s2n_stuffer *out) { uint8_t record_protocol_version = conn->actual_protocol_version; @@ -170,11 +175,13 @@ int s2n_record_write_protocol_version(struct s2n_connection *conn, struct s2n_st * use that assumed value here in case we are talking to a legacy * server that expects TLS1.0. * - * If we are requesting early data, we can assume that we aren't talking to - * a legacy server as a legacy server would not know how to handle early data. + * Both TLS 1.3 early data and a deserialized connection will + * send data without the server_protocol_version being known. However, + * the record type would be set to APPLICATION_DATA in their cases + * so this check is avoided. **/ if (conn->server_protocol_version == s2n_unknown_protocol_version - && conn->early_data_state != S2N_EARLY_DATA_REQUESTED) { + && record_type == TLS_HANDSHAKE) { record_protocol_version = MIN(record_protocol_version, S2N_TLS10); } @@ -352,10 +359,9 @@ int s2n_record_writev(struct s2n_connection *conn, uint8_t content_type, const s POSIX_GUARD(s2n_stuffer_init(&record_stuffer, &record_blob)); /* Now that we know the length, start writing the record */ - POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, is_tls13_record ? - /* tls 1.3 opaque type */ TLS_APPLICATION_DATA : - /* actual content_type */ content_type)); - POSIX_GUARD(s2n_record_write_protocol_version(conn, &record_stuffer)); + uint8_t record_type = RECORD_TYPE(is_tls13_record, content_type); + POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, record_type)); + POSIX_GUARD(s2n_record_write_protocol_version(conn, record_type, &record_stuffer)); /* First write a header that has the payload length, this is for the MAC */ POSIX_GUARD(s2n_stuffer_write_uint16(&record_stuffer, data_bytes_to_take)); diff --git a/tls/s2n_recv.c b/tls/s2n_recv.c index 323c4b5edef..a3d29274437 100644 --- a/tls/s2n_recv.c +++ b/tls/s2n_recv.c @@ -36,11 +36,32 @@ #include "utils/s2n_safety.h" #include "utils/s2n_socket.h" +S2N_RESULT s2n_recv_in_init(struct s2n_connection *conn, uint32_t written, uint32_t total) +{ + RESULT_ENSURE_REF(conn); + + /* If we're going to initialize conn->in to point to more memory than + * is actually readable, make sure that the additional memory exists. + */ + RESULT_ENSURE_LTE(written, total); + uint32_t remaining = total - written; + RESULT_ENSURE_LTE(remaining, s2n_stuffer_space_remaining(&conn->buffer_in)); + + uint8_t *data = s2n_stuffer_raw_read(&conn->buffer_in, written); + RESULT_ENSURE_REF(data); + RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in)); + RESULT_GUARD_POSIX(s2n_blob_init(&conn->in.blob, data, total)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&conn->in, written)); + return S2N_RESULT_OK; +} + S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length) { while (s2n_stuffer_data_available(output) < length) { uint32_t remaining = length - s2n_stuffer_data_available(output); - + if (conn->recv_buffering) { + remaining = MAX(remaining, s2n_stuffer_space_remaining(output)); + } errno = 0; int r = s2n_connection_recv_stuffer(output, conn, remaining); if (r == 0) { @@ -53,6 +74,20 @@ S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *ou return S2N_RESULT_OK; } +static S2N_RESULT s2n_recv_buffer_in(struct s2n_connection *conn, size_t min_size) +{ + RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_LARGE_FRAGMENT_LENGTH)); + uint32_t buffer_in_available = s2n_stuffer_data_available(&conn->buffer_in); + if (buffer_in_available < min_size) { + uint32_t remaining = min_size - buffer_in_available; + if (s2n_stuffer_space_remaining(&conn->buffer_in) < remaining) { + RESULT_GUARD_POSIX(s2n_stuffer_shift(&conn->buffer_in)); + } + RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, min_size)); + } + return S2N_RESULT_OK; +} + int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int *isSSLv2) { *isSSLv2 = 0; @@ -67,11 +102,17 @@ int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int *record_type = TLS_APPLICATION_DATA; return S2N_SUCCESS; } - POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->in, S2N_LARGE_FRAGMENT_LENGTH)); /* Read the record until we at least have a header */ POSIX_GUARD(s2n_stuffer_reread(&conn->header_in)); - POSIX_GUARD_RESULT(s2n_read_in_bytes(conn, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + uint32_t header_available = s2n_stuffer_data_available(&conn->header_in); + if (header_available < S2N_TLS_RECORD_HEADER_LENGTH) { + uint32_t header_remaining = S2N_TLS_RECORD_HEADER_LENGTH - header_available; + s2n_result ret = s2n_recv_buffer_in(conn, header_remaining); + uint32_t header_read = MIN(header_remaining, s2n_stuffer_data_available(&conn->buffer_in)); + POSIX_GUARD(s2n_stuffer_copy(&conn->buffer_in, &conn->header_in, header_read)); + POSIX_GUARD_RESULT(ret); + } uint16_t fragment_length = 0; @@ -84,7 +125,14 @@ int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int } /* Read enough to have the whole record */ - POSIX_GUARD_RESULT(s2n_read_in_bytes(conn, &conn->in, fragment_length)); + uint32_t fragment_available = s2n_stuffer_data_available(&conn->in); + if (fragment_available < fragment_length || fragment_length == 0) { + POSIX_GUARD(s2n_stuffer_rewind_read(&conn->buffer_in, fragment_available)); + s2n_result ret = s2n_recv_buffer_in(conn, fragment_length); + uint32_t fragment_read = MIN(fragment_length, s2n_stuffer_data_available(&conn->buffer_in)); + POSIX_GUARD_RESULT(s2n_recv_in_init(conn, fragment_read, fragment_length)); + POSIX_GUARD_RESULT(ret); + } if (*isSSLv2) { return 0; @@ -269,3 +317,11 @@ uint32_t s2n_peek(struct s2n_connection *conn) return s2n_stuffer_data_available(&conn->in); } + +uint32_t s2n_peek_buffered(struct s2n_connection *conn) +{ + if (conn == NULL) { + return 0; + } + return s2n_stuffer_data_available(&conn->buffer_in); +} diff --git a/tls/s2n_renegotiate.c b/tls/s2n_renegotiate.c index 84765e44722..34389b5a741 100644 --- a/tls/s2n_renegotiate.c +++ b/tls/s2n_renegotiate.c @@ -72,6 +72,11 @@ int s2n_renegotiate_wipe(struct s2n_connection *conn) POSIX_ENSURE(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_INVALID_STATE); POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_INVALID_STATE); + /* buffer_in might contain data needed to read the next records. */ + DEFER_CLEANUP(struct s2n_stuffer buffer_in = conn->buffer_in, s2n_stuffer_free); + conn->buffer_in = (struct s2n_stuffer){ 0 }; + POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->buffer_in, 0)); + /* Save the crypto parameters. * We need to continue encrypting / decrypting with the old secure parameters. */ @@ -152,6 +157,8 @@ int s2n_renegotiate_wipe(struct s2n_connection *conn) conn->recv = recv_fn; conn->recv_io_context = recv_ctx; conn->secure_renegotiation = secure_renegotiation; + conn->buffer_in = buffer_in; + ZERO_TO_DISABLE_DEFER_CLEANUP(buffer_in); conn->handshake.renegotiation = true; return S2N_SUCCESS; diff --git a/tls/s2n_security_policies.c b/tls/s2n_security_policies.c index 4bdec0f1414..824fab4ae37 100644 --- a/tls/s2n_security_policies.c +++ b/tls/s2n_security_policies.c @@ -28,7 +28,7 @@ const struct s2n_security_policy security_policy_20170210 = { .ecc_preferences = &s2n_ecc_preferences_20140601, }; -const struct s2n_security_policy security_policy_default_tls13 = { +const struct s2n_security_policy security_policy_20240417 = { .minimum_protocol_version = S2N_TLS10, .cipher_preferences = &cipher_preferences_20210831, .kem_preferences = &kem_preferences_null, @@ -43,7 +43,7 @@ const struct s2n_security_policy security_policy_default_tls13 = { * * Supports TLS1.2 */ -const struct s2n_security_policy security_policy_default_fips = { +const struct s2n_security_policy security_policy_20240416 = { .minimum_protocol_version = S2N_TLS12, .cipher_preferences = &cipher_preferences_default_fips, .kem_preferences = &kem_preferences_null, @@ -69,6 +69,19 @@ const struct s2n_security_policy security_policy_20230317 = { }, }; +const struct s2n_security_policy security_policy_20240331 = { + .minimum_protocol_version = S2N_TLS12, + .cipher_preferences = &cipher_preferences_20240331, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20230317, + .certificate_signature_preferences = &s2n_signature_preferences_20230317, + .ecc_preferences = &s2n_ecc_preferences_20201021, + .rules = { + [S2N_PERFECT_FORWARD_SECRECY] = true, + [S2N_FIPS_140_3] = true, + }, +}; + const struct s2n_security_policy security_policy_20190801 = { .minimum_protocol_version = S2N_TLS10, .cipher_preferences = &cipher_preferences_20190801, @@ -1059,9 +1072,12 @@ const struct s2n_security_policy security_policy_null = { struct s2n_security_policy_selection security_policy_selection[] = { { .version = "default", .security_policy = &security_policy_20170210, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, - { .version = "default_tls13", .security_policy = &security_policy_default_tls13, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, - { .version = "default_fips", .security_policy = &security_policy_default_fips, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, + { .version = "default_tls13", .security_policy = &security_policy_20240417, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, + { .version = "default_fips", .security_policy = &security_policy_20240416, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "20230317", .security_policy = &security_policy_20230317, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, + { .version = "20240331", .security_policy = &security_policy_20240331, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, + { .version = "20240417", .security_policy = &security_policy_20240417, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, + { .version = "20240416", .security_policy = &security_policy_20240416, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, { .version = "ELBSecurityPolicy-TLS-1-0-2015-04", .security_policy = &security_policy_elb_2015_04, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, /* Not a mistake. TLS-1-0-2015-05 and 2016-08 are equivalent */ { .version = "ELBSecurityPolicy-TLS-1-0-2015-05", .security_policy = &security_policy_elb_2016_08, .ecc_extension_required = 0, .pq_kem_extension_required = 0 }, diff --git a/tls/s2n_security_policies.h b/tls/s2n_security_policies.h index 90bfda1296b..f7e7680b28c 100644 --- a/tls/s2n_security_policies.h +++ b/tls/s2n_security_policies.h @@ -113,8 +113,9 @@ extern const struct s2n_security_policy security_policy_20190214_gcm; extern const struct s2n_security_policy security_policy_20190801; extern const struct s2n_security_policy security_policy_20190802; extern const struct s2n_security_policy security_policy_20230317; -extern const struct s2n_security_policy security_policy_default_tls13; -extern const struct s2n_security_policy security_policy_default_fips; +extern const struct s2n_security_policy security_policy_20240331; +extern const struct s2n_security_policy security_policy_20240417; +extern const struct s2n_security_policy security_policy_20240416; extern const struct s2n_security_policy security_policy_rfc9151; extern const struct s2n_security_policy security_policy_test_all; diff --git a/tls/s2n_server_cert_request.c b/tls/s2n_server_cert_request.c index 79929a32cda..ad36af6dd54 100644 --- a/tls/s2n_server_cert_request.c +++ b/tls/s2n_server_cert_request.c @@ -16,6 +16,7 @@ #include "api/s2n.h" #include "crypto/s2n_certificate.h" #include "error/s2n_errno.h" +#include "extensions/s2n_cert_authorities.h" #include "extensions/s2n_extension_list.h" #include "stuffer/s2n_stuffer.h" #include "tls/s2n_cipher_suites.h" @@ -174,10 +175,8 @@ int s2n_cert_req_send(struct s2n_connection *conn) POSIX_GUARD_RESULT(s2n_signature_algorithms_supported_list_send(conn, out)); } - /* RFC 5246 7.4.4 - If the certificate_authorities list is empty, then the - * client MAY send any certificate of the appropriate ClientCertificateType */ - uint16_t acceptable_cert_authorities_len = 0; - POSIX_GUARD(s2n_stuffer_write_uint16(out, acceptable_cert_authorities_len)); + /* Before TLS1.3, certificate_authorities is part of the message instead of an extension */ + POSIX_GUARD(s2n_cert_authorities_send(conn, out)); - return 0; + return S2N_SUCCESS; } diff --git a/tls/s2n_signature_scheme.c b/tls/s2n_signature_scheme.c index c4bab44d865..5da3639c2cc 100644 --- a/tls/s2n_signature_scheme.c +++ b/tls/s2n_signature_scheme.c @@ -110,8 +110,7 @@ const struct s2n_signature_scheme s2n_ecdsa_sha256 = { .hash_alg = S2N_HASH_SHA256, .sig_alg = S2N_SIGNATURE_ECDSA, .libcrypto_nid = NID_ecdsa_with_SHA256, - .signature_curve = NULL, /* Decided by supported_groups Extension in TLS 1.2 and before */ - .maximum_protocol_version = S2N_TLS12, /* TLS1.3 requires a signature curve */ + .signature_curve = &s2n_ecc_curve_secp256r1, /* Hardcoded for TLS 1.3 */ }; const struct s2n_signature_scheme s2n_ecdsa_sha384 = { @@ -119,8 +118,7 @@ const struct s2n_signature_scheme s2n_ecdsa_sha384 = { .hash_alg = S2N_HASH_SHA384, .sig_alg = S2N_SIGNATURE_ECDSA, .libcrypto_nid = NID_ecdsa_with_SHA384, - .signature_curve = NULL, /* Decided by supported_groups Extension in TLS 1.2 and before */ - .maximum_protocol_version = S2N_TLS12, /* TLS1.3 requires a signature curve */ + .signature_curve = &s2n_ecc_curve_secp384r1, /* Hardcoded for TLS 1.3 */ }; const struct s2n_signature_scheme s2n_ecdsa_sha512 = { @@ -128,38 +126,7 @@ const struct s2n_signature_scheme s2n_ecdsa_sha512 = { .hash_alg = S2N_HASH_SHA512, .sig_alg = S2N_SIGNATURE_ECDSA, .libcrypto_nid = NID_ecdsa_with_SHA512, - .signature_curve = NULL, /* Decided by supported_groups Extension in TLS 1.2 and before */ - .maximum_protocol_version = S2N_TLS12, /* TLS1.3 requires a signature curve */ -}; - -/* TLS 1.3 Compatible ECDSA Schemes */ -/* In TLS 1.3 the two byte IANA value also defines the Curve to use for signing */ - -const struct s2n_signature_scheme s2n_ecdsa_secp256r1_sha256 = { - .iana_value = TLS_SIGNATURE_SCHEME_ECDSA_SECP256R1_SHA256, - .hash_alg = S2N_HASH_SHA256, - .sig_alg = S2N_SIGNATURE_ECDSA, - .libcrypto_nid = NID_ecdsa_with_SHA256, - .signature_curve = &s2n_ecc_curve_secp256r1, /* Hardcoded as of TLS 1.3 */ - .minimum_protocol_version = S2N_TLS13, -}; - -const struct s2n_signature_scheme s2n_ecdsa_secp384r1_sha384 = { - .iana_value = TLS_SIGNATURE_SCHEME_ECDSA_SECP384R1_SHA384, - .hash_alg = S2N_HASH_SHA384, - .sig_alg = S2N_SIGNATURE_ECDSA, - .libcrypto_nid = NID_ecdsa_with_SHA384, - .signature_curve = &s2n_ecc_curve_secp384r1, /* Hardcoded as of TLS 1.3 */ - .minimum_protocol_version = S2N_TLS13, -}; - -const struct s2n_signature_scheme s2n_ecdsa_secp521r1_sha512 = { - .iana_value = TLS_SIGNATURE_SCHEME_ECDSA_SECP521R1_SHA512, - .hash_alg = S2N_HASH_SHA512, - .sig_alg = S2N_SIGNATURE_ECDSA, - .libcrypto_nid = NID_ecdsa_with_SHA512, - .signature_curve = &s2n_ecc_curve_secp521r1, /* Hardcoded as of TLS 1.3 */ - .minimum_protocol_version = S2N_TLS13, + .signature_curve = &s2n_ecc_curve_secp521r1, /* Hardcoded for TLS 1.3 */ }; /** @@ -228,11 +195,9 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20140601[] = { &s2n_rsa_pkcs1_sha512, &s2n_rsa_pkcs1_sha224, - /* ECDSA - TLS 1.2 */ - &s2n_ecdsa_sha256, /* same iana value as TLS 1.3 s2n_ecdsa_secp256r1_sha256 */ - &s2n_ecdsa_secp256r1_sha256, - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ - &s2n_ecdsa_secp384r1_sha384, + /* ECDSA */ + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, &s2n_ecdsa_sha512, &s2n_ecdsa_sha224, @@ -257,11 +222,9 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20200207[] = { &s2n_rsa_pkcs1_sha512, &s2n_rsa_pkcs1_sha224, - /* ECDSA - TLS 1.2 */ - &s2n_ecdsa_sha256, /* same iana value as TLS 1.3 s2n_ecdsa_secp256r1_sha256 */ - &s2n_ecdsa_secp256r1_sha256, - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ - &s2n_ecdsa_secp384r1_sha384, + /* ECDSA */ + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, &s2n_ecdsa_sha512, &s2n_ecdsa_sha224, @@ -280,9 +243,9 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_default_fips[] &s2n_rsa_pkcs1_sha384, &s2n_rsa_pkcs1_sha512, - /* ECDSA - TLS 1.2 */ - &s2n_ecdsa_sha256, /* same iana value as TLS 1.3 s2n_ecdsa_secp256r1_sha256 */ - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ + /* ECDSA */ + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, &s2n_ecdsa_sha512, &s2n_ecdsa_sha224, }; @@ -307,15 +270,10 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20230317[] = { &s2n_rsa_pkcs1_sha512, /* TLS1.2 with ECDSA */ - &s2n_ecdsa_sha256, /* same iana value as TLS 1.3 s2n_ecdsa_secp256r1_sha256 */ - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, &s2n_ecdsa_sha512, - /* TLS1.3 with ECDSA */ - &s2n_ecdsa_secp256r1_sha256, - &s2n_ecdsa_secp384r1_sha384, - &s2n_ecdsa_secp521r1_sha512, - /* TLS1.3 with RSA-PSS */ &s2n_rsa_pss_pss_sha256, &s2n_rsa_pss_pss_sha384, @@ -327,7 +285,6 @@ const struct s2n_signature_preferences s2n_signature_preferences_20230317 = { .signature_schemes = s2n_sig_scheme_pref_list_20230317, }; -/* Add s2n_ecdsa_secp521r1_sha512 */ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20201021[] = { /* RSA PSS */ &s2n_rsa_pss_pss_sha256, @@ -343,13 +300,10 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20201021[] = { &s2n_rsa_pkcs1_sha512, &s2n_rsa_pkcs1_sha224, - /* ECDSA - TLS 1.2 */ - &s2n_ecdsa_sha256, /* same iana value as TLS 1.3 s2n_ecdsa_secp256r1_sha256 */ - &s2n_ecdsa_secp256r1_sha256, - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ - &s2n_ecdsa_secp384r1_sha384, - &s2n_ecdsa_sha512, /* same iana value as TLS 1.3 s2n_ecdsa_secp521r1_sha512 */ - &s2n_ecdsa_secp521r1_sha512, + /* ECDSA */ + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, + &s2n_ecdsa_sha512, &s2n_ecdsa_sha224, /* SHA-1 Legacy */ @@ -393,11 +347,9 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20201110[] = { &s2n_rsa_pkcs1_sha512, &s2n_rsa_pkcs1_sha224, - /* ECDSA - TLS 1.2 */ - &s2n_ecdsa_sha256, /* same iana value as TLS 1.3 s2n_ecdsa_secp256r1_sha256 */ - &s2n_ecdsa_secp256r1_sha256, - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ - &s2n_ecdsa_secp384r1_sha384, + /* ECDSA */ + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, &s2n_ecdsa_sha512, &s2n_ecdsa_sha224, }; @@ -413,8 +365,8 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20210816[] = { &s2n_rsa_pkcs1_sha384, &s2n_rsa_pkcs1_sha512, - /* ECDSA - TLS 1.2 */ - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ + /* ECDSA */ + &s2n_ecdsa_sha384, &s2n_ecdsa_sha512, }; @@ -424,15 +376,12 @@ const struct s2n_signature_preferences s2n_signature_preferences_20210816 = { }; const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_rfc9151[] = { - /* ECDSA - TLS 1.3 */ - &s2n_ecdsa_secp384r1_sha384, + /* ECDSA */ + &s2n_ecdsa_sha384, /* RSA PSS - TLS 1.3 */ &s2n_rsa_pss_pss_sha384, - /* ECDSA - TLS 1.2 */ - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ - /* RSA */ &s2n_rsa_pss_rsae_sha384, @@ -440,8 +389,8 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_rfc9151[] = { }; const struct s2n_signature_scheme* const s2n_cert_sig_scheme_pref_list_rfc9151[] = { - /* ECDSA - TLS 1.3 */ - &s2n_ecdsa_secp384r1_sha384, + /* ECDSA */ + &s2n_ecdsa_sha384, /* RSA PSS * https://github.com/aws/s2n-tls/issues/3435 @@ -454,9 +403,6 @@ const struct s2n_signature_scheme* const s2n_cert_sig_scheme_pref_list_rfc9151[] * support rsa_pss. */ - /* ECDSA - TLS 1.2 */ - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ - /* RSA */ &s2n_rsa_pkcs1_sha384, }; @@ -487,12 +433,9 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_test_all_fips[ &s2n_rsa_pkcs1_sha224, /* ECDSA */ - &s2n_ecdsa_sha256, /* same iana value as TLS 1.3 s2n_ecdsa_secp256r1_sha256 */ - &s2n_ecdsa_secp256r1_sha256, - &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ - &s2n_ecdsa_secp384r1_sha384, - &s2n_ecdsa_sha512, /* same iana value as TLS 1.3 s2n_ecdsa_secp521r1_sha512 */ - &s2n_ecdsa_secp521r1_sha512, + &s2n_ecdsa_sha256, + &s2n_ecdsa_sha384, + &s2n_ecdsa_sha512, &s2n_ecdsa_sha224, }; diff --git a/tls/s2n_signature_scheme.h b/tls/s2n_signature_scheme.h index c8143f452aa..139318344e2 100644 --- a/tls/s2n_signature_scheme.h +++ b/tls/s2n_signature_scheme.h @@ -30,7 +30,7 @@ struct s2n_signature_scheme { uint8_t maximum_protocol_version; uint16_t libcrypto_nid; - /* Curve is only specified for ECDSA Signatures */ + /* Curve is only defined for TLS1.3 ECDSA Signatures */ struct s2n_ecc_named_curve const *signature_curve; }; @@ -51,18 +51,12 @@ extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha256; extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha384; extern const struct s2n_signature_scheme s2n_rsa_pkcs1_sha512; -/* TLS 1.2 Compatible ECDSA Schemes */ extern const struct s2n_signature_scheme s2n_ecdsa_sha1; extern const struct s2n_signature_scheme s2n_ecdsa_sha224; extern const struct s2n_signature_scheme s2n_ecdsa_sha256; extern const struct s2n_signature_scheme s2n_ecdsa_sha384; extern const struct s2n_signature_scheme s2n_ecdsa_sha512; -/* TLS 1.3 Compatible ECDSA Schemes */ -extern const struct s2n_signature_scheme s2n_ecdsa_secp256r1_sha256; -extern const struct s2n_signature_scheme s2n_ecdsa_secp384r1_sha384; -extern const struct s2n_signature_scheme s2n_ecdsa_secp521r1_sha512; - /* RSA PSS */ /* * Use RSA-PSS-RSAE instead of RSA-PSS-PSS in order to work with older certificates. diff --git a/tls/s2n_tls.h b/tls/s2n_tls.h index ff7670535b7..3f7b6344ee2 100644 --- a/tls/s2n_tls.h +++ b/tls/s2n_tls.h @@ -85,6 +85,7 @@ S2N_RESULT s2n_handshake_parse_header(struct s2n_stuffer *io, uint8_t *message_t int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int *isSSLv2); S2N_RESULT s2n_sendv_with_offset_total_size(const struct iovec *bufs, ssize_t count, ssize_t offs, ssize_t *total_size_out); +S2N_RESULT s2n_recv_in_init(struct s2n_connection *conn, uint32_t written, uint32_t size); extern uint16_t mfl_code_to_length[5]; diff --git a/tls/s2n_tls13_key_schedule.c b/tls/s2n_tls13_key_schedule.c index 5d9f69dbd1b..e86660874a0 100644 --- a/tls/s2n_tls13_key_schedule.c +++ b/tls/s2n_tls13_key_schedule.c @@ -27,8 +27,8 @@ *# The notation "K_{send,recv} = foo" means "set *# the send/recv key to the given key". */ -#define K_send(conn, secret_type) RESULT_GUARD(s2n_set_key(conn, secret_type, (conn)->mode)) -#define K_recv(conn, secret_type) RESULT_GUARD(s2n_set_key(conn, secret_type, S2N_PEER_MODE((conn)->mode))) +#define K_send(conn, secret_type) RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, secret_type, (conn)->mode)) +#define K_recv(conn, secret_type) RESULT_GUARD(s2n_tls13_key_schedule_set_key(conn, secret_type, S2N_PEER_MODE((conn)->mode))) static const struct s2n_blob s2n_zero_length_context = { 0 }; @@ -119,7 +119,7 @@ static S2N_RESULT s2n_tls13_key_schedule_get_keying_material( return S2N_RESULT_OK; } -static S2N_RESULT s2n_set_key(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, s2n_mode mode) +S2N_RESULT s2n_tls13_key_schedule_set_key(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, s2n_mode mode) { RESULT_ENSURE_REF(conn); RESULT_ENSURE_REF(conn->secure); diff --git a/tls/s2n_tls13_key_schedule.h b/tls/s2n_tls13_key_schedule.h index c7270faaabc..13dac5a25c4 100644 --- a/tls/s2n_tls13_key_schedule.h +++ b/tls/s2n_tls13_key_schedule.h @@ -15,6 +15,7 @@ #pragma once +#include "tls/s2n_tls13_secrets.h" #include "utils/s2n_result.h" struct s2n_key_material; @@ -23,3 +24,4 @@ S2N_RESULT s2n_tls13_key_schedule_generate_key_material(struct s2n_connection *c S2N_RESULT s2n_tls13_key_schedule_update(struct s2n_connection *conn); S2N_RESULT s2n_tls13_key_schedule_reset(struct s2n_connection *conn); +S2N_RESULT s2n_tls13_key_schedule_set_key(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, s2n_mode mode); diff --git a/tls/s2n_tls_parameters.h b/tls/s2n_tls_parameters.h index cb565aa3072..02a12211aca 100644 --- a/tls/s2n_tls_parameters.h +++ b/tls/s2n_tls_parameters.h @@ -124,7 +124,7 @@ *# quic_transport_parameters(0x39), (65535) *# } ExtensionType; */ -#define TLS_QUIC_TRANSPORT_PARAMETERS 0x39 +#define TLS_EXTENSION_QUIC_TRANSPORT_PARAMETERS 0x39 /* TLS SignatureScheme (Backwards compatible with SigHash and SigAlg values above) */ /* Defined here: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme */ @@ -147,18 +147,14 @@ #define TLS_SIGNATURE_SCHEME_ECDSA_SHA384 0x0503 #define TLS_SIGNATURE_SCHEME_ECDSA_SHA512 0x0603 -/* TLS 1.3 ECDSA Signature Schemes */ -#define TLS_SIGNATURE_SCHEME_ECDSA_SECP256R1_SHA256 0x0403 -#define TLS_SIGNATURE_SCHEME_ECDSA_SECP384R1_SHA384 0x0503 -#define TLS_SIGNATURE_SCHEME_ECDSA_SECP521R1_SHA512 0x0603 -#define TLS_SIGNATURE_SCHEME_RSA_PSS_RSAE_SHA256 0x0804 -#define TLS_SIGNATURE_SCHEME_RSA_PSS_RSAE_SHA384 0x0805 -#define TLS_SIGNATURE_SCHEME_RSA_PSS_RSAE_SHA512 0x0806 -#define TLS_SIGNATURE_SCHEME_ED25519 0x0807 -#define TLS_SIGNATURE_SCHEME_ED448 0x0808 -#define TLS_SIGNATURE_SCHEME_RSA_PSS_PSS_SHA256 0x0809 -#define TLS_SIGNATURE_SCHEME_RSA_PSS_PSS_SHA384 0x080A -#define TLS_SIGNATURE_SCHEME_RSA_PSS_PSS_SHA512 0x080B +#define TLS_SIGNATURE_SCHEME_RSA_PSS_RSAE_SHA256 0x0804 +#define TLS_SIGNATURE_SCHEME_RSA_PSS_RSAE_SHA384 0x0805 +#define TLS_SIGNATURE_SCHEME_RSA_PSS_RSAE_SHA512 0x0806 +#define TLS_SIGNATURE_SCHEME_ED25519 0x0807 +#define TLS_SIGNATURE_SCHEME_ED448 0x0808 +#define TLS_SIGNATURE_SCHEME_RSA_PSS_PSS_SHA256 0x0809 +#define TLS_SIGNATURE_SCHEME_RSA_PSS_PSS_SHA384 0x080A +#define TLS_SIGNATURE_SCHEME_RSA_PSS_PSS_SHA512 0x080B #define TLS_SIGNATURE_SCHEME_LEN 2 #define TLS_SIGNATURE_SCHEME_LIST_MAX_LEN 64 diff --git a/utils/s2n_blob.h b/utils/s2n_blob.h index deba566466d..8f9481affbc 100644 --- a/utils/s2n_blob.h +++ b/utils/s2n_blob.h @@ -71,3 +71,7 @@ int S2N_RESULT_MUST_USE s2n_blob_slice(const struct s2n_blob *b, struct s2n_blob #define S2N_BLOB_FROM_HEX(name, hex) \ s2n_stack_blob(name, (sizeof(hex) - 1) / 2, (sizeof(hex) - 1) / 2); \ POSIX_GUARD(s2n_hex_string_to_bytes((const uint8_t *) hex, &name)); + +#define S2N_RESULT_BLOB_FROM_HEX(name, hex) \ + RESULT_STACK_BLOB(name, (sizeof(hex) - 1) / 2, (sizeof(hex) - 1) / 2); \ + RESULT_GUARD_POSIX(s2n_hex_string_to_bytes((const uint8_t *) hex, &name));