From 25e3a8fd77eed704e1002878e02b047bf21a1c45 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 6 May 2025 09:18:09 -0400 Subject: [PATCH 1/8] Cargo: rustls 0.23.25 -> 0.23.27 https://github.com/rustls/rustls/releases/tag/v%2F0.23.26 https://github.com/rustls/rustls/releases/tag/v%2F0.23.27 --- Cargo.lock | 16 ++++++++-------- librustls/Cargo.toml | 2 +- librustls/build.rs | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c96e2732..7c0173e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,9 +159,9 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.3" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1233,9 +1233,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.26" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "aws-lc-rs", "brotli", @@ -1325,9 +1325,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" dependencies = [ "aws-lc-rs", "ring", diff --git a/librustls/Cargo.toml b/librustls/Cargo.toml index 1907ca6d..90e7a94e 100644 --- a/librustls/Cargo.toml +++ b/librustls/Cargo.toml @@ -29,7 +29,7 @@ prefer-post-quantum = ["aws-lc-rs", "rustls/prefer-post-quantum"] [dependencies] # Keep in sync with RUSTLS_CRATE_VERSION in build.rs -rustls = { version = "0.23.25", default-features = false, features = ["std", "tls12"] } +rustls = { version = "0.23.27", default-features = false, features = ["std", "tls12"] } webpki = { workspace = true } libc = { workspace = true } log = { workspace = true } diff --git a/librustls/build.rs b/librustls/build.rs index 0a8962d4..4af344a0 100644 --- a/librustls/build.rs +++ b/librustls/build.rs @@ -8,7 +8,7 @@ use std::{env, fs, path::PathBuf}; // because doing so would require a heavy-weight deserialization lib dependency // (and it couldn't be a _dev_ dep for use in a build script) or doing brittle // by-hand parsing. -const RUSTLS_CRATE_VERSION: &str = "0.23.25"; +const RUSTLS_CRATE_VERSION: &str = "0.23.27"; fn main() { let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); From 3eb4fa64348ccb941935ff95c3095907c9c42053 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 6 May 2025 09:24:54 -0400 Subject: [PATCH 2/8] librustls: tidy rustls_client_connection_new docs Simplify language a bit, use more backticks, describe success condition up-front. --- librustls/src/client.rs | 13 ++++++------- librustls/src/rustls.h | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/librustls/src/client.rs b/librustls/src/client.rs index 94fa50bf..acc9374e 100644 --- a/librustls/src/client.rs +++ b/librustls/src/client.rs @@ -743,17 +743,16 @@ impl rustls_client_config { } } - /// Create a new rustls_connection containing a client connection and return - /// it in the output parameter `conn_out`. + /// Create a new client `rustls_connection`. + /// + /// If this returns `RUSTLS_RESULT_OK`, the memory pointed to by `conn_out` is modified to + /// point at a valid `rustls_connection`. The caller now owns the `rustls_connection` + /// and must call `rustls_connection_free` when done with it. /// /// If this returns an error code, the memory pointed to by `conn_out` remains /// unchanged. /// - /// If this returns a non-error, the memory pointed to by `conn_out` - /// is modified to point at a valid `rustls_connection`. The caller now owns - /// the `rustls_connection` and must call `rustls_connection_free` when done with it. - /// - /// The server_name parameter can contain a hostname or an IP address in + /// The `server_name` parameter can contain a hostname or an IP address in /// textual form (IPv4 or IPv6). This function will return an error if it /// cannot be parsed as one of those types. #[no_mangle] diff --git a/librustls/src/rustls.h b/librustls/src/rustls.h index 34dd0f28..0ac04aa6 100644 --- a/librustls/src/rustls.h +++ b/librustls/src/rustls.h @@ -1580,17 +1580,16 @@ bool rustls_client_config_fips(const struct rustls_client_config *config); void rustls_client_config_free(const struct rustls_client_config *config); /** - * Create a new rustls_connection containing a client connection and return - * it in the output parameter `conn_out`. + * Create a new client `rustls_connection`. + * + * If this returns `RUSTLS_RESULT_OK`, the memory pointed to by `conn_out` is modified to + * point at a valid `rustls_connection`. The caller now owns the `rustls_connection` + * and must call `rustls_connection_free` when done with it. * * If this returns an error code, the memory pointed to by `conn_out` remains * unchanged. * - * If this returns a non-error, the memory pointed to by `conn_out` - * is modified to point at a valid `rustls_connection`. The caller now owns - * the `rustls_connection` and must call `rustls_connection_free` when done with it. - * - * The server_name parameter can contain a hostname or an IP address in + * The `server_name` parameter can contain a hostname or an IP address in * textual form (IPv4 or IPv6). This function will return an error if it * cannot be parsed as one of those types. */ From e7ec846b484f1f655f724178192656d6f67a3107 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 6 May 2025 09:38:49 -0400 Subject: [PATCH 3/8] librustls: remove duplicative out param null check In `rustls_client_connection_new` we're using the pattern of constructing a `&mut *mut` out reference from a `*mut *mut` pointer input using the `try_mut_from_ptr_ptr!` macro, which checks that the param isn't `NULL`. This means an earlier explicit check isn't needed and can be removed for clarity. --- librustls/src/client.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/librustls/src/client.rs b/librustls/src/client.rs index acc9374e..83c1a524 100644 --- a/librustls/src/client.rs +++ b/librustls/src/client.rs @@ -762,9 +762,6 @@ impl rustls_client_config { conn_out: *mut *mut rustls_connection, ) -> rustls_result { ffi_panic_boundary! { - if conn_out.is_null() { - return rustls_result::NullParameter; - } let server_name = unsafe { if server_name.is_null() { return rustls_result::NullParameter; From fcea2b4a881d2b0b0934e347a7119bc93878dca1 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 6 May 2025 09:41:41 -0400 Subject: [PATCH 4/8] librustls: prefer let/else in rustls_client_connection_new --- librustls/src/client.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/librustls/src/client.rs b/librustls/src/client.rs index 83c1a524..a8ef647e 100644 --- a/librustls/src/client.rs +++ b/librustls/src/client.rs @@ -770,13 +770,12 @@ impl rustls_client_config { }; let config = try_clone_arc!(config); let conn_out = try_mut_from_ptr_ptr!(conn_out); - let server_name = match server_name.to_str() { - Ok(s) => s, - Err(std::str::Utf8Error { .. }) => return rustls_result::InvalidDnsNameError, + + let Ok(server_name) = server_name.to_str() else { + return rustls_result::InvalidDnsNameError; }; - let server_name = match server_name.try_into() { - Ok(sn) => sn, - Err(_) => return rustls_result::InvalidDnsNameError, + let Ok(server_name) = server_name.try_into() else { + return rustls_result::InvalidDnsNameError; }; let client = ClientConnection::new(config, server_name).unwrap(); From 4c646599a277fd54e305b9e494874ee8da46a20e Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 6 May 2025 09:46:07 -0400 Subject: [PATCH 5/8] librustls: inline rustls_client_connection_new bindings --- librustls/src/client.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/librustls/src/client.rs b/librustls/src/client.rs index a8ef647e..3faa0b9d 100644 --- a/librustls/src/client.rs +++ b/librustls/src/client.rs @@ -768,22 +768,19 @@ impl rustls_client_config { } CStr::from_ptr(server_name) }; - let config = try_clone_arc!(config); - let conn_out = try_mut_from_ptr_ptr!(conn_out); - let Ok(server_name) = server_name.to_str() else { return rustls_result::InvalidDnsNameError; }; let Ok(server_name) = server_name.try_into() else { return rustls_result::InvalidDnsNameError; }; - let client = ClientConnection::new(config, server_name).unwrap(); - // We've succeeded. Put the client on the heap, and transfer ownership - // to the caller. After this point, we must return rustls_result::Ok so the - // caller knows it is responsible for this memory. - let c = Connection::from_client(client); - set_boxed_mut_ptr(conn_out, c); + set_boxed_mut_ptr( + try_mut_from_ptr_ptr!(conn_out), + Connection::from_client( + ClientConnection::new(try_clone_arc!(config), server_name).unwrap(), + ), + ); rustls_result::Ok } } From 239bb7560c70c44f2ad5c4ae0d71576394bac589 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 6 May 2025 09:54:33 -0400 Subject: [PATCH 6/8] librustls: add rustls_client_connection_new_alpn This allows constructing a client `rustls_connection` with custom ALPN protocol support that differs from the base `rustls_client_config`. --- librustls/src/client.rs | 172 ++++++++++++++++++++++++++++++++-------- librustls/src/rustls.h | 36 +++++++++ 2 files changed, 175 insertions(+), 33 deletions(-) diff --git a/librustls/src/client.rs b/librustls/src/client.rs index 3faa0b9d..8e18ae15 100644 --- a/librustls/src/client.rs +++ b/librustls/src/client.rs @@ -749,6 +749,9 @@ impl rustls_client_config { /// point at a valid `rustls_connection`. The caller now owns the `rustls_connection` /// and must call `rustls_connection_free` when done with it. /// + /// Uses the `rustls_client_config` to determine ALPN protocol support. Prefer + /// `rustls_client_connection_new_alpn` to customize this per-connection. + /// /// If this returns an error code, the memory pointed to by `conn_out` remains /// unchanged. /// @@ -762,28 +765,96 @@ impl rustls_client_config { conn_out: *mut *mut rustls_connection, ) -> rustls_result { ffi_panic_boundary! { - let server_name = unsafe { - if server_name.is_null() { - return rustls_result::NullParameter; - } - CStr::from_ptr(server_name) - }; - let Ok(server_name) = server_name.to_str() else { - return rustls_result::InvalidDnsNameError; - }; - let Ok(server_name) = server_name.try_into() else { - return rustls_result::InvalidDnsNameError; - }; + Self::rustls_client_connection_new_alpn_inner( + config, + server_name, + try_clone_arc!(config).alpn_protocols.clone(), + conn_out, + ) + } + } - set_boxed_mut_ptr( - try_mut_from_ptr_ptr!(conn_out), - Connection::from_client( - ClientConnection::new(try_clone_arc!(config), server_name).unwrap(), - ), - ); - rustls_result::Ok + /// Create a new client `rustls_connection` with custom ALPN protocols. + /// + /// Operates the same as `rustls_client_connection_new`, but allows specifying + /// custom per-connection ALPN protocols instead of inheriting ALPN protocols + /// from the `rustls_clinet_config`. + /// + /// If this returns `RUSTLS_RESULT_OK`, the memory pointed to by `conn_out` is modified to + /// point at a valid `rustls_connection`. The caller now owns the `rustls_connection` + /// and must call `rustls_connection_free` when done with it. + /// + /// If this returns an error code, the memory pointed to by `conn_out` remains + /// unchanged. + /// + /// The `server_name` parameter can contain a hostname or an IP address in + /// textual form (IPv4 or IPv6). This function will return an error if it + /// cannot be parsed as one of those types. + /// + /// `alpn_protocols` must point to a buffer of `rustls_slice_bytes` (built by the caller) + /// with `alpn_protocols_len` elements. Each element of the buffer must be a `rustls_slice_bytes` + /// whose data field points to a single ALPN protocol ID. This function makes a copy of the + /// data in `alpn_protocols` and does not retain any pointers, so the caller can free the + /// pointed-to memory after calling. + /// + /// Standard ALPN protocol IDs are defined at + /// . + #[no_mangle] + pub extern "C" fn rustls_client_connection_new_alpn( + config: *const rustls_client_config, + server_name: *const c_char, + alpn_protocols: *const rustls_slice_bytes, + alpn_protocols_len: size_t, + conn_out: *mut *mut rustls_connection, + ) -> rustls_result { + ffi_panic_boundary! { + let raw_protocols = try_slice!(alpn_protocols, alpn_protocols_len); + let mut alpn_protocols = Vec::with_capacity(raw_protocols.len()); + for p in raw_protocols { + alpn_protocols.push(try_slice!(p.data, p.len).to_vec()); + } + + Self::rustls_client_connection_new_alpn_inner( + config, + server_name, + alpn_protocols, + conn_out, + ) } } + + fn rustls_client_connection_new_alpn_inner( + config: *const rustls_client_config, + server_name: *const c_char, + alpn_protocols: Vec>, + conn_out: *mut *mut rustls_connection, + ) -> rustls_result { + let server_name = unsafe { + if server_name.is_null() { + return rustls_result::NullParameter; + } + CStr::from_ptr(server_name) + }; + let Ok(server_name) = server_name.to_str() else { + return rustls_result::InvalidDnsNameError; + }; + let Ok(server_name) = server_name.try_into() else { + return rustls_result::InvalidDnsNameError; + }; + + set_boxed_mut_ptr( + try_mut_from_ptr_ptr!(conn_out), + Connection::from_client( + ClientConnection::new_with_alpn( + try_clone_arc!(config), + server_name, + alpn_protocols, + ) + .unwrap(), + ), + ); + rustls_result::Ok + } } #[cfg(all(test, any(feature = "ring", feature = "aws-lc-rs")))] @@ -830,20 +901,8 @@ mod tests { #[test] #[cfg_attr(miri, ignore)] fn test_client_connection_new() { - let builder = rustls_client_config_builder::rustls_client_config_builder_new(); - let mut verifier = null_mut(); - let result = - rustls_server_cert_verifier::rustls_platform_server_cert_verifier(&mut verifier); - assert_eq!(result, rustls_result::Ok); - assert!(!verifier.is_null()); - rustls_client_config_builder::rustls_client_config_builder_set_server_verifier( - builder, verifier, - ); - let mut config = null(); - let result = - rustls_client_config_builder::rustls_client_config_builder_build(builder, &mut config); - assert_eq!(result, rustls_result::Ok); - assert!(!config.is_null()); + let (config, verifier) = test_config(); + let mut conn = null_mut(); let result = rustls_client_config::rustls_client_connection_new( config, @@ -887,6 +946,53 @@ mod tests { rustls_server_cert_verifier::rustls_server_cert_verifier_free(verifier); } + // Build a client connection w/ custom ALPN and ensure no error occurs. + #[test] + #[cfg_attr(miri, ignore)] + fn test_client_connection_new_alpn() { + let (config, verifier) = test_config(); + let alpn_protocols = [ + rustls_slice_bytes::from(b"h2".as_ref()), + rustls_slice_bytes::from(b"http/1.1".as_ref()), + ]; + + let mut conn = null_mut(); + let result = rustls_client_config::rustls_client_connection_new_alpn( + config, + "example.com\0".as_ptr() as *const c_char, + alpn_protocols.as_ptr(), + alpn_protocols.len() as size_t, + &mut conn, + ); + if !matches!(result, rustls_result::Ok) { + panic!("expected RUSTLS_RESULT_OK, got {result:?}"); + } + + rustls_connection::rustls_connection_free(conn); + rustls_server_cert_verifier::rustls_server_cert_verifier_free(verifier); + } + + fn test_config() -> ( + *const rustls_client_config, + *mut rustls_server_cert_verifier, + ) { + let builder = rustls_client_config_builder::rustls_client_config_builder_new(); + let mut verifier = null_mut(); + let result = + rustls_server_cert_verifier::rustls_platform_server_cert_verifier(&mut verifier); + assert_eq!(result, rustls_result::Ok); + assert!(!verifier.is_null()); + rustls_client_config_builder::rustls_client_config_builder_set_server_verifier( + builder, verifier, + ); + let mut config = null(); + let result = + rustls_client_config_builder::rustls_client_config_builder_build(builder, &mut config); + assert_eq!(result, rustls_result::Ok); + assert!(!config.is_null()); + (config, verifier) + } + #[test] #[cfg_attr(miri, ignore)] fn test_client_connection_new_ipaddress() { diff --git a/librustls/src/rustls.h b/librustls/src/rustls.h index 0ac04aa6..db54b7d7 100644 --- a/librustls/src/rustls.h +++ b/librustls/src/rustls.h @@ -1586,6 +1586,9 @@ void rustls_client_config_free(const struct rustls_client_config *config); * point at a valid `rustls_connection`. The caller now owns the `rustls_connection` * and must call `rustls_connection_free` when done with it. * + * Uses the `rustls_client_config` to determine ALPN protocol support. Prefer + * `rustls_client_connection_new_alpn` to customize this per-connection. + * * If this returns an error code, the memory pointed to by `conn_out` remains * unchanged. * @@ -1597,6 +1600,39 @@ rustls_result rustls_client_connection_new(const struct rustls_client_config *co const char *server_name, struct rustls_connection **conn_out); +/** + * Create a new client `rustls_connection` with custom ALPN protocols. + * + * Operates the same as `rustls_client_connection_new`, but allows specifying + * custom per-connection ALPN protocols instead of inheriting ALPN protocols + * from the `rustls_clinet_config`. + * + * If this returns `RUSTLS_RESULT_OK`, the memory pointed to by `conn_out` is modified to + * point at a valid `rustls_connection`. The caller now owns the `rustls_connection` + * and must call `rustls_connection_free` when done with it. + * + * If this returns an error code, the memory pointed to by `conn_out` remains + * unchanged. + * + * The `server_name` parameter can contain a hostname or an IP address in + * textual form (IPv4 or IPv6). This function will return an error if it + * cannot be parsed as one of those types. + * + * `alpn_protocols` must point to a buffer of `rustls_slice_bytes` (built by the caller) + * with `alpn_protocols_len` elements. Each element of the buffer must be a `rustls_slice_bytes` + * whose data field points to a single ALPN protocol ID. This function makes a copy of the + * data in `alpn_protocols` and does not retain any pointers, so the caller can free the + * pointed-to memory after calling. + * + * Standard ALPN protocol IDs are defined at + * . + */ +rustls_result rustls_client_connection_new_alpn(const struct rustls_client_config *config, + const char *server_name, + const struct rustls_slice_bytes *alpn_protocols, + size_t alpn_protocols_len, + struct rustls_connection **conn_out); + /** * Set the userdata pointer associated with this connection. This will be passed * to any callbacks invoked by the connection, if you've set up callbacks in the config. From 89545be1d0d23883b9884d7b801b663054da33d6 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 6 May 2025 09:57:18 -0400 Subject: [PATCH 7/8] librustls: map_invalid_certificate_error key purpose ctx Handle the new upstream `CertificateError::InvalidPurposeContext` error type as `CertInvalidPurpose`. --- librustls/src/error.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/librustls/src/error.rs b/librustls/src/error.rs index 301f37e9..5832ceff 100644 --- a/librustls/src/error.rs +++ b/librustls/src/error.rs @@ -743,7 +743,9 @@ fn map_invalid_certificate_error(err: CertificateError) -> rustls_result { CertificateError::NotValidForName | CertificateError::NotValidForNameContext { .. } => { CertNotValidForName } - CertificateError::InvalidPurpose => CertInvalidPurpose, + CertificateError::InvalidPurpose | CertificateError::InvalidPurposeContext { .. } => { + CertInvalidPurpose + } CertificateError::ApplicationVerificationFailure => CertApplicationVerificationFailure, _ => CertOtherError, } From 9cf6f2a17514b946f4231d96a848329bdd2168ec Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Tue, 6 May 2025 10:23:24 -0400 Subject: [PATCH 8/8] librustls: prefer post quantum by default Matches the change upstream in rustls 0.23.27 --- README.md | 7 +++---- librustls/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5e2a855d..8a628598 100644 --- a/README.md +++ b/README.md @@ -101,11 +101,10 @@ platforms see the upstream documentation: #### Post-Quantum X25519MLKEM768 Key Exchange Post-quantum-secure key exchange using [X25519MLKEM768][] is supported when using the `aws-lc-rs` -cryptography provider. At this time default support places `X25519MLKEM768` at a lower negotiation priority. +cryptography provider and offered by default at the highest priority. -By enabling the `prefer-post-quantum` feature flag the `X25519MLKEM768` key exchange will be used as the most -preferred key exchange algorithm. We expect to add this feature to the crate's default features in a future -release. +By disabling the `prefer-post-quantum` feature flag the `X25519MLKEM768` key exchange will be +offered at a lower negotiation priority. [X25519MLKEM768]: https://datatracker.ietf.org/doc/draft-kwiatkowski-tls-ecdhe-mlkem diff --git a/librustls/Cargo.toml b/librustls/Cargo.toml index 90e7a94e..01a0a5b7 100644 --- a/librustls/Cargo.toml +++ b/librustls/Cargo.toml @@ -12,7 +12,7 @@ links = "rustls_ffi" rust-version = "1.71" [features] -default = ["aws-lc-rs"] +default = ["aws-lc-rs", "prefer-post-quantum"] # Enable this feature when building as Rust dependency. It inhibits the # default behavior of capturing the global logger, which only works when # built using the Makefile, which passes -C metadata=rustls-ffi to avoid