Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions librustls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }
Expand Down
2 changes: 1 addition & 1 deletion librustls/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
190 changes: 144 additions & 46 deletions librustls/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -743,17 +743,19 @@ 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.
///
/// 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.
///
/// 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]
Expand All @@ -763,35 +765,96 @@ impl rustls_client_config {
conn_out: *mut *mut rustls_connection,
) -> rustls_result {
ffi_panic_boundary! {
if conn_out.is_null() {
return rustls_result::NullParameter;
Self::rustls_client_connection_new_alpn_inner(
config,
server_name,
try_clone_arc!(config).alpn_protocols.clone(),
conn_out,
)
}
}

/// Create a new client `rustls_connection` with custom ALPN protocols.
Comment thread
cpu marked this conversation as resolved.
///
/// 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
/// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>.
#[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());
}
let server_name = unsafe {
if server_name.is_null() {
return rustls_result::NullParameter;
}
CStr::from_ptr(server_name)
};
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 server_name = match server_name.try_into() {
Ok(sn) => sn,
Err(_) => 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);
rustls_result::Ok
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<Vec<u8>>,
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")))]
Expand Down Expand Up @@ -838,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,
Expand Down Expand Up @@ -895,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() {
Expand Down
4 changes: 3 additions & 1 deletion librustls/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
49 changes: 42 additions & 7 deletions librustls/src/rustls.h
Original file line number Diff line number Diff line change
Expand Up @@ -1580,24 +1580,59 @@ 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.
*
* 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.
*
* 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.
*/
rustls_result rustls_client_connection_new(const struct rustls_client_config *config,
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
* <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>.
*/
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.
Expand Down