Skip to content

Commit

Permalink
Merge branch 'main' into sidetrail-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
goatgoose committed Apr 12, 2024
2 parents 582b942 + 4220a4d commit 8f7a302
Show file tree
Hide file tree
Showing 58 changed files with 2,944 additions and 149 deletions.
80 changes: 80 additions & 0 deletions api/s2n.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
1 change: 1 addition & 0 deletions api/unstable/ktls.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/

/**
Expand Down
2 changes: 2 additions & 0 deletions api/unstable/renegotiate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions bindings/rust/s2n-tls-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn env<N: AsRef<str>>(name: N) -> String {

fn option_env<N: AsRef<str>>(name: N) -> Option<String> {
let name = name.as_ref();
eprintln!("cargo:rerun-if-env-changed={}", name);
println!("cargo:rerun-if-env-changed={}", name);
std::env::var(name).ok()
}

Expand Down Expand Up @@ -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"));
Expand Down
2 changes: 1 addition & 1 deletion bindings/rust/s2n-tls-sys/templates/Cargo.template
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 2 additions & 2 deletions bindings/rust/s2n-tls-tokio/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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]
Expand Down
5 changes: 3 additions & 2 deletions bindings/rust/s2n-tls/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"]
Expand All @@ -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"

Expand Down
49 changes: 49 additions & 0 deletions bindings/rust/s2n-tls/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<s2n_connection>,
}
Expand Down Expand Up @@ -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<KeyUpdateCount, Error> {
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
Expand Down
16 changes: 16 additions & 0 deletions bindings/rust/s2n-tls/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,19 @@ impl TryFrom<s2n_tls_hash_algorithm::Type> for HashAlgorithm {
Ok(version)
}
}

#[non_exhaustive]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum PeerKeyUpdate {
KeyUpdateNotRequested,
KeyUpdatedRequested,
}

impl From<PeerKeyUpdate> 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,
}
}
}
36 changes: 36 additions & 0 deletions bindings/rust/s2n-tls/src/testing/s2n_tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
}
3 changes: 2 additions & 1 deletion docs/usage-guide/topics/ch06-security-policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand All @@ -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.

Expand Down
5 changes: 4 additions & 1 deletion error/s2n_errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
3 changes: 3 additions & 0 deletions error/s2n_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion stuffer/s2n_stuffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
Loading

0 comments on commit 8f7a302

Please sign in to comment.