diff --git a/Cargo.lock b/Cargo.lock index 2d4031f5..9dfeb7c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,6 +211,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clang-sys" version = "1.8.1" @@ -386,6 +392,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -405,6 +422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", @@ -769,6 +787,33 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ktls" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5b79b6ec9c9d56656298df28f92dbaf2e83816be6d12c5b896461087e52d701" +dependencies = [ + "futures-util", + "ktls-sys", + "libc", + "memoffset", + "nix", + "num_enum", + "pin-project-lite", + "rustls", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tokio-rustls", + "tracing", +] + +[[package]] +name = "ktls-sys" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ed84c81d133bc00e291085cff51c15cb07d3cede0aade1f2d82dd33df82d7e7" + [[package]] name = "lazy_static" version = "1.5.0" @@ -853,6 +898,15 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -898,6 +952,19 @@ dependencies = [ "uuid", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nom" version = "7.1.3" @@ -918,6 +985,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "object" version = "0.36.7" @@ -1024,6 +1113,15 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.97" @@ -1208,6 +1306,7 @@ dependencies = [ name = "rustls-ffi" version = "0.15.0" dependencies = [ + "ktls", "libc", "log", "regex", @@ -1669,9 +1768,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.34" diff --git a/librustls/Cargo.toml b/librustls/Cargo.toml index 91890158..560f8821 100644 --- a/librustls/Cargo.toml +++ b/librustls/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/rustls/rustls-ffi" categories = ["network-programming", "cryptography"] edition = "2021" links = "rustls_ffi" -rust-version = "1.71" +rust-version = "1.75" [features] default = ["aws-lc-rs", "prefer-post-quantum"] @@ -27,6 +27,7 @@ aws-lc-rs = ["rustls/aws-lc-rs", "webpki/aws-lc-rs"] cert_compression = ["rustls/brotli", "rustls/zlib"] fips = ["rustls/fips", "webpki/aws-lc-rs-fips"] prefer-post-quantum = ["aws-lc-rs", "rustls/prefer-post-quantum"] +ktls = [] [dependencies] # Keep in sync with RUSTLS_CRATE_VERSION in build.rs @@ -36,6 +37,9 @@ libc = { workspace = true } log = { workspace = true } rustls-platform-verifier = { workspace = true } +[target.'cfg(target_os = "linux")'.dependencies] +ktls = "6.0.2" + [lib] name = "rustls_ffi" crate-type = ["lib", "staticlib"] diff --git a/librustls/cbindgen.toml b/librustls/cbindgen.toml index 5f500d06..11d44250 100644 --- a/librustls/cbindgen.toml +++ b/librustls/cbindgen.toml @@ -41,7 +41,8 @@ include = ["rustls_tls_version"] "feature = aws-lc-rs" = "DEFINE_AWS_LC_RS" "feature = ring" = "DEFINE_RING" "feature = fips" = "DEFINE_FIPS" +"feature = ktls" = "DEFINE_KTLS" [parse.expand] crates = ["rustls-ffi"] -features = ["read_buf", "aws-lc-rs", "ring", "fips"] +features = ["read_buf", "aws-lc-rs", "ring", "fips", "ktls"] diff --git a/librustls/cmake/options.cmake b/librustls/cmake/options.cmake index e559b426..6e08a128 100644 --- a/librustls/cmake/options.cmake +++ b/librustls/cmake/options.cmake @@ -25,6 +25,15 @@ option( "Whether to enable aws-lc-rs and prefer post-quantum key exchange support" ) +option(KTLS "Whether to enable kTLS") + +if (KTLS AND (APPLE OR WIN32)) + message( + FATAL_ERROR + "kTLS is not supported with MacOS or Windows" + ) +endif() + option(DYN_LINK "Use dynamic linking for rustls library" OFF) if(DYN_LINK AND FIPS AND (APPLE OR WIN32)) @@ -54,6 +63,10 @@ if(PREFER_POST_QUANTUM) list(APPEND CARGO_FEATURES --features=prefer-post-quantum) endif() +if(KTLS) + list(APPEND CARGO_FEATURES --features=ktls) +endif() + # By default w/ Makefile or Ninja generators (e.g. Linux/MacOS CLI) # the `CMAKE_BUILD_TYPE` is "" when using the C/C++ project tooling. # diff --git a/librustls/src/client.rs b/librustls/src/client.rs index 8e18ae15..fd0a5409 100644 --- a/librustls/src/client.rs +++ b/librustls/src/client.rs @@ -54,6 +54,7 @@ pub(crate) struct ClientConfigBuilder { cert_resolver: Option>, key_log: Option>, ech_mode: Option, + enable_secret_extraction: bool, } impl Default for ClientConfigBuilder { @@ -72,6 +73,7 @@ impl Default for ClientConfigBuilder { cert_resolver: None, key_log: None, ech_mode: None, + enable_secret_extraction: false, } } } @@ -272,6 +274,18 @@ impl rustls_client_config_builder { } } + /// Enable or disable secret extraction, e.g. for kTLS. + #[no_mangle] + pub extern "C" fn rustls_client_config_builder_set_enable_secret_extraction( + config: *mut rustls_client_config_builder, + enable: bool, + ) { + ffi_panic_boundary! { + let config = try_mut_from_ptr!(config); + config.enable_secret_extraction = enable; + } + } + /// Provide the configuration a list of certificates where the connection /// will select the first one that is compatible with the server's signature /// verification capabilities. @@ -529,6 +543,7 @@ impl rustls_client_config_builder { }; config.alpn_protocols = builder.alpn_protocols; config.enable_sni = builder.enable_sni; + config.enable_secret_extraction = builder.enable_secret_extraction; if let Some(key_log) = builder.key_log { config.key_log = key_log; diff --git a/librustls/src/connection.rs b/librustls/src/connection.rs index 9b4dba14..89dbbfe5 100644 --- a/librustls/src/connection.rs +++ b/librustls/src/connection.rs @@ -10,6 +10,8 @@ use rustls::{ClientConnection, ServerConnection}; use crate::certificate::rustls_certificate; use crate::enums::rustls_handshake_kind; use crate::error::{map_error, rustls_io_result, rustls_result}; +#[cfg(feature = "ktls")] +use crate::ffi::try_box_from_ptr; use crate::ffi::{ box_castable, free_box, try_callback, try_mut_from_ptr, try_ref_from_ptr, try_slice, try_slice_mut, @@ -97,6 +99,17 @@ box_castable! { pub struct rustls_connection(Connection); } +#[cfg(feature = "ktls")] +pub type rustls_ktls_secrets_callback = Option< + unsafe extern "C" fn( + userdata: *mut c_void, + rx_buf: *const c_void, + rx_n: size_t, + tx_buf: *const c_void, + tx_n: size_t, + ) -> rustls_io_result, +>; + impl rustls_connection { /// 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. @@ -302,6 +315,47 @@ impl rustls_connection { } } + /// Extract secrets and consume connection. Must not be called twice + /// with the same value. Secrets are borrowed in `callback` and should + /// not be retained across calls. The `userdata` parameter is passed + /// through directly to `callback`. Note that this is distinct from the + /// `userdata` parameter set with `rustls_connection_set_userdata`. + #[cfg(feature = "ktls")] + #[no_mangle] + pub extern "C" fn rustls_connection_ktls_secrets( + conn: *mut rustls_connection, + callback: rustls_ktls_secrets_callback, + userdata: *mut c_void, + ) -> rustls_result { + ffi_panic_boundary! { + if conn.is_null() { + return rustls_result::NullParameter; + } + let tls = try_box_from_ptr!(conn).conn; + let suite = tls.negotiated_cipher_suite().unwrap(); + match tls.dangerous_extract_secrets() { + Ok(extracted_secrets) => { + let Ok(rx) = ktls::CryptoInfo::from_rustls(suite, extracted_secrets.rx) else { + return rustls_result::HandshakeNotComplete; + }; + let Ok(tx) = ktls::CryptoInfo::from_rustls(suite, extracted_secrets.tx) else { + return rustls_result::HandshakeNotComplete; + }; + let callback = try_callback!(callback); + let result = unsafe { + callback(userdata, rx.as_ptr(), rx.size(), tx.as_ptr(), tx.size()) + }; + match result.0 { + 0 => rustls_result::Ok, + _ => rustls_result::KtlsUserCallback, + } + } + Err(rustls::Error::HandshakeNotComplete) => rustls_result::HandshakeNotComplete, + Err(_) => rustls_result::KtlsSecretExtractionDisabled, + } + } + } + /// Sets a limit on the internal buffers used to buffer unsent plaintext (prior /// to completing the TLS handshake) and unsent TLS records. By default, there /// is no limit. The limit can be set at any time, even if the current buffer diff --git a/librustls/src/error.rs b/librustls/src/error.rs index 333525dc..6d133017 100644 --- a/librustls/src/error.rs +++ b/librustls/src/error.rs @@ -211,7 +211,11 @@ u32_enum_builder! { // From InvalidEncryptedClientHello, with fields that get flattened. InvalidEncryptedClientHelloInvalidConfigList => 7700, InvalidEncryptedClientHelloNoCompatibleConfig => 7701, - InvalidEncryptedClientHelloSniRequired => 7702 + InvalidEncryptedClientHelloSniRequired => 7702, + + KtlsCompatibility => 7800, + KtlsUserCallback => 7801, + KtlsSecretExtractionDisabled => 7802 } } @@ -551,6 +555,9 @@ impl Display for rustls_result { InvalidEncryptedClientHelloSniRequired => { Error::InvalidEncryptedClientHello(EncryptedClientHelloError::SniRequired).fmt(f) } + KtlsCompatibility => write!(f, "kTLS compatibility error"), + KtlsUserCallback => write!(f, "kTLS user callback"), + KtlsSecretExtractionDisabled => write!(f, "kTLS secret extraction disabled"), } } } diff --git a/librustls/src/rustls.h b/librustls/src/rustls.h index d3d9faa3..718b5528 100644 --- a/librustls/src/rustls.h +++ b/librustls/src/rustls.h @@ -192,6 +192,9 @@ enum rustls_result { RUSTLS_RESULT_INVALID_ENCRYPTED_CLIENT_HELLO_INVALID_CONFIG_LIST = 7700, RUSTLS_RESULT_INVALID_ENCRYPTED_CLIENT_HELLO_NO_COMPATIBLE_CONFIG = 7701, RUSTLS_RESULT_INVALID_ENCRYPTED_CLIENT_HELLO_SNI_REQUIRED = 7702, + RUSTLS_RESULT_KTLS_COMPATIBILITY = 7800, + RUSTLS_RESULT_KTLS_USER_CALLBACK = 7801, + RUSTLS_RESULT_KTLS_SECRET_EXTRACTION_DISABLED = 7802, }; typedef uint32_t rustls_result; @@ -663,6 +666,14 @@ typedef rustls_io_result (*rustls_write_vectored_callback)(void *userdata, size_t count, size_t *out_n); +#if defined(DEFINE_KTLS) +typedef rustls_io_result (*rustls_ktls_secrets_callback)(void *userdata, + const void *rx_buf, + size_t rx_n, + const void *tx_buf, + size_t tx_n); +#endif + /** * Any context information the callback will receive when invoked. */ @@ -1455,6 +1466,12 @@ rustls_result rustls_client_config_builder_set_alpn_protocols(struct rustls_clie void rustls_client_config_builder_set_enable_sni(struct rustls_client_config_builder *config, bool enable); +/** + * Enable or disable secret extraction, e.g. for kTLS. + */ +void rustls_client_config_builder_set_enable_secret_extraction(struct rustls_client_config_builder *config, + bool enable); + /** * Provide the configuration a list of certificates where the connection * will select the first one that is compatible with the server's signature @@ -1763,6 +1780,19 @@ bool rustls_connection_is_handshaking(const struct rustls_connection *conn); */ enum rustls_handshake_kind rustls_connection_handshake_kind(const struct rustls_connection *conn); +#if defined(DEFINE_KTLS) +/** + * Extract secrets and consume connection. Must not be called twice + * with the same value. Secrets are borrowed in `callback` and should + * not be retained across calls. The `userdata` parameter is passed + * through directly to `callback`. Note that this is distinct from the + * `userdata` parameter set with `rustls_connection_set_userdata`. + */ +rustls_result rustls_connection_ktls_secrets(struct rustls_connection *conn, + rustls_ktls_secrets_callback callback, + void *userdata); +#endif + /** * Sets a limit on the internal buffers used to buffer unsent plaintext (prior * to completing the TLS handshake) and unsent TLS records. By default, there @@ -2362,6 +2392,12 @@ rustls_result rustls_server_config_builder_set_key_log(struct rustls_server_conf rustls_keylog_log_callback log_cb, rustls_keylog_will_log_callback will_log_cb); +/** + * Enable or disable secret extraction, e.g. for kTLS. + */ +void rustls_server_config_builder_set_enable_secret_extraction(struct rustls_server_config_builder *config, + bool enable); + /** * "Free" a server_config_builder without building it into a rustls_server_config. * diff --git a/librustls/src/server.rs b/librustls/src/server.rs index e0df7e97..e000af4c 100644 --- a/librustls/src/server.rs +++ b/librustls/src/server.rs @@ -56,6 +56,7 @@ pub(crate) struct ServerConfigBuilder { alpn_protocols: Vec>, ignore_client_order: Option, key_log: Option>, + enable_secret_extraction: bool, } arc_castable! { @@ -89,6 +90,7 @@ impl rustls_server_config_builder { alpn_protocols: vec![], ignore_client_order: None, key_log: None, + enable_secret_extraction: false, }; to_boxed_mut_ptr(builder) } @@ -144,6 +146,7 @@ impl rustls_server_config_builder { alpn_protocols: vec![], ignore_client_order: None, key_log: None, + enable_secret_extraction: false, }; set_boxed_mut_ptr(builder_out, builder); rustls_result::Ok @@ -231,6 +234,18 @@ impl rustls_server_config_builder { } } + /// Enable or disable secret extraction, e.g. for kTLS. + #[no_mangle] + pub extern "C" fn rustls_server_config_builder_set_enable_secret_extraction( + config: *mut rustls_server_config_builder, + enable: bool, + ) { + ffi_panic_boundary! { + let config = try_mut_from_ptr!(config); + config.enable_secret_extraction = enable; + } + } + /// "Free" a server_config_builder without building it into a rustls_server_config. /// /// Normally builders are built into rustls_server_configs via `rustls_server_config_builder_build` @@ -371,6 +386,8 @@ impl rustls_server_config_builder { config.key_log = key_log; } + config.enable_secret_extraction = builder.enable_secret_extraction; + set_arc_mut_ptr(config_out, config); rustls_result::Ok }