diff --git a/api/s2n.h b/api/s2n.h index 0f78ff458e5..1d35e657ed8 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. * 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..9935ea24279 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_serialized_connection_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..13501299a15 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.2" 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..21289d65775 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.2" 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.2", 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..18b4b1bc870 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.2" 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.2", path = "../s2n-tls-sys", features = ["internal"] } pin-project-lite = "0.2" hex = "0.4" diff --git a/bindings/rust/s2n-tls/src/connection.rs b/bindings/rust/s2n-tls/src/connection.rs index c0754e7388d..dd175680281 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, } @@ -267,6 +276,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 diff --git a/bindings/rust/s2n-tls/src/enums.rs b/bindings/rust/s2n-tls/src/enums.rs index 5bc32d47b14..3116dfb2323 100644 --- a/bindings/rust/s2n-tls/src/enums.rs +++ b/bindings/rust/s2n-tls/src/enums.rs @@ -177,3 +177,19 @@ 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, + } + } +} 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/docs/usage-guide/topics/ch06-security-policies.md b/docs/usage-guide/topics/ch06-security-policies.md index 2eea2376080..bc76e35ce45 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 | | @@ -42,7 +43,7 @@ The following chart maps the security policy version to protocol version and cip 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. -"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. +"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. diff --git a/error/s2n_errno.c b/error/s2n_errno.c index 7b770681758..2052160a999 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_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..6c10f5dcaaa 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_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/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_client_auth_handshake_test.c b/tests/unit/s2n_client_auth_handshake_test.c index 3b29c02604c..242757110a0 100644 --- a/tests/unit/s2n_client_auth_handshake_test.c +++ b/tests/unit/s2n_client_auth_handshake_test.c @@ -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_connection_serialize_test.c b/tests/unit/s2n_connection_serialize_test.c new file mode 100644 index 00000000000..688f9992013 --- /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_serialized_connection_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_serialized_connection_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_serialized_connection_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_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_serialized_connection_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_serialized_connection_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_serialized_connection_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_serialized_connection_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_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..ea1baa1c332 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_serialized_connection_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_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..fea639e7225 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) @@ -53,6 +54,60 @@ static S2N_RESULT s2n_test_security_policies_compatible(const struct s2n_securit return S2N_RESULT_OK; } +static S2N_RESULT s2n_test_get_missing_duplicate_signature_scheme( + const struct s2n_signature_scheme *const *policy_schemes, size_t policy_schemes_count, + uint8_t minimum_policy_version, uint8_t maximum_policy_version, + const struct s2n_signature_scheme **duplicate) +{ + if (policy_schemes_count > 0) { + RESULT_ENSURE_REF(policy_schemes); + } + RESULT_ENSURE_REF(duplicate); + *duplicate = NULL; + + const struct s2n_signature_preferences *all_schemes = security_policy_test_all.signature_preferences; + + /* Check all schemes in target policy */ + for (int i = 0; i < policy_schemes_count; i++) { + const struct s2n_signature_scheme *from_policy = policy_schemes[i]; + EXPECT_NOT_NULL(from_policy); + + /* Check if duplicates exist for the scheme */ + for (size_t j = 0; j < all_schemes->count; j++) { + const struct s2n_signature_scheme *from_all = all_schemes->signature_schemes[j]; + EXPECT_NOT_NULL(from_all); + + /* Skip if not a duplicate */ + if (from_all == from_policy) { + continue; + } else if (from_all->iana_value != from_policy->iana_value) { + continue; + } else if (from_all->maximum_protocol_version + && from_all->maximum_protocol_version < minimum_policy_version) { + continue; + } else if (from_all->minimum_protocol_version + && from_all->minimum_protocol_version > maximum_policy_version) { + continue; + } + *duplicate = from_all; + + /* Check whether duplicate is also in the target policy */ + for (size_t k = 0; k < policy_schemes_count; k++) { + const struct s2n_signature_scheme *possible_match = policy_schemes[k]; + EXPECT_NOT_NULL(possible_match); + if (*duplicate == possible_match) { + *duplicate = NULL; + break; + } + } + if (*duplicate) { + return S2N_RESULT_OK; + } + } + } + return S2N_RESULT_OK; +} + int main(int argc, char **argv) { BEGIN_TEST(); @@ -460,6 +515,7 @@ int main(int argc, char **argv) "20190121", "20190122", "20201021", + "20240331", "test_all_ecdsa", "test_ecdsa_priority", "test_all_tls12", @@ -478,6 +534,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", @@ -973,8 +1030,97 @@ 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); }; }; + /* Policies must include all signature schemes that share an IANA value */ + { + for (int i = 0; security_policy_selection[i].version != NULL; i++) { + security_policy = security_policy_selection[i].security_policy; + EXPECT_NOT_NULL(security_policy); + const uint8_t max_protocol_version = security_policy_selection[i].supports_tls13 ? + s2n_highest_protocol_version : + S2N_TLS12; + + /* Check signature scheme preferences */ + { + const struct s2n_signature_scheme *duplicate = NULL; + EXPECT_OK(s2n_test_get_missing_duplicate_signature_scheme( + security_policy->signature_preferences->signature_schemes, + security_policy->signature_preferences->count, + security_policy->minimum_protocol_version, + max_protocol_version, + &duplicate)); + + if (duplicate) { + fprintf(stderr, "Policy: %s Scheme: %04x\n", + security_policy_selection[i].version, + duplicate->iana_value); + FAIL_MSG("Missing signature scheme"); + } + } + + /* Check certificate signature scheme preferences */ + if (security_policy->certificate_signature_preferences) { + const struct s2n_signature_scheme *duplicate = NULL; + EXPECT_OK(s2n_test_get_missing_duplicate_signature_scheme( + security_policy->certificate_signature_preferences->signature_schemes, + security_policy->certificate_signature_preferences->count, + security_policy->minimum_protocol_version, + max_protocol_version, + &duplicate)); + + if (duplicate) { + fprintf(stderr, "Policy: %s Scheme: %04x\n", + security_policy_selection[i].version, + duplicate->iana_value); + FAIL_MSG("Missing certificate signature scheme"); + } + } + } + } + END_TEST(); } 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..11c5cfad052 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) @@ -165,11 +165,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 +218,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 */ @@ -478,7 +481,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 +595,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/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_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..fc41648ab20 100644 --- a/tls/s2n_config.c +++ b/tls/s2n_config.c @@ -1127,6 +1127,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 +1223,17 @@ int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, return S2N_SUCCESS; } + +int s2n_config_set_serialized_connection_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..9880dfab587 100644 --- a/tls/s2n_config.h +++ b/tls/s2n_config.h @@ -54,6 +54,11 @@ typedef enum { S2N_LIB_OWNED, } s2n_cert_ownership; +typedef enum { + S2N_SERIALIZED_CONN_NONE = 0, + S2N_SERIALIZED_CONN_V1 = 1 +} s2n_serialization_version; + struct s2n_config { unsigned use_tickets : 1; @@ -208,6 +213,23 @@ 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; }; S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config); @@ -229,3 +251,6 @@ S2N_RESULT s2n_config_wall_clock(struct s2n_config *config, uint64_t *output); * in `security_policy` */ S2N_RESULT s2n_config_validate_loaded_certificates(const struct s2n_config *config, const struct s2n_security_policy *security_policy); + +/* APIs that will be moved to s2n.h when the serialized connection feature is released */ +int s2n_config_set_serialized_connection_version(struct s2n_config *config, s2n_serialization_version version); 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..4d97ab4b564 --- /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_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..e7b4ae21f55 --- /dev/null +++ b/tls/s2n_connection_serialize.h @@ -0,0 +1,30 @@ +/* + * 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) + +/* APIs that will be moved to s2n.h when the connection serialize feature is released */ +int s2n_connection_serialization_length(struct s2n_connection *conn, uint32_t *length); +int s2n_connection_serialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); +int s2n_connection_deserialize(struct s2n_connection *conn, uint8_t *buffer, uint32_t buffer_length); 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_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..6ed13d71d53 100644 --- a/tls/s2n_security_policies.c +++ b/tls/s2n_security_policies.c @@ -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, @@ -1062,6 +1075,7 @@ struct s2n_security_policy_selection security_policy_selection[] = { { .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 = "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 = "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..54efe6052a3 100644 --- a/tls/s2n_security_policies.h +++ b/tls/s2n_security_policies.h @@ -113,6 +113,7 @@ 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_20240331; 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_rfc9151; diff --git a/tls/s2n_signature_scheme.c b/tls/s2n_signature_scheme.c index c4bab44d865..bd2234a84eb 100644 --- a/tls/s2n_signature_scheme.c +++ b/tls/s2n_signature_scheme.c @@ -263,6 +263,7 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20200207[] = { &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ &s2n_ecdsa_secp384r1_sha384, &s2n_ecdsa_sha512, + &s2n_ecdsa_secp521r1_sha512, &s2n_ecdsa_sha224, /* SHA-1 Legacy */ @@ -399,6 +400,7 @@ const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_20201110[] = { &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ &s2n_ecdsa_secp384r1_sha384, &s2n_ecdsa_sha512, + &s2n_ecdsa_secp521r1_sha512, &s2n_ecdsa_sha224, }; 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/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));