Skip to content

Commit 359a247

Browse files
committed
librustls: add kTLS support
Add `rustls_connection_ktls_secrets`, which consumes `rustls_connection` to generate tx and rx secrets for kTLS. Secrets are borrowed and passed to a callback. ```c static rustls_io_result ktls_secrets_callback(void *userdata, const uint8_t *rx_buf, size_t rx_n, const uint8_t *tx_buf, size_t tx_n) { int result; int fd = *(int*)userdata; result = setsockopt(fd, SOL_TCP, TCP_ULP, "tls", 4); if (result < 0) return result; result = setsockopt(fd, SOL_TLS, TLS_RX, rx_buf, rx_n); if (result < 0) return result; result = setsockopt(fd, SOL_TLS, TLS_TX, tx_buf, tx_n); if (result < 0) return result; return 0; } ``` ```c int fd /* = ... */; struct rustls_connection * connection /* = ... */; while (rustls_connection_is_handshaking(connection)) { /* process reads and writes */ } while (rustls_connection_wants_write(connection)) { /* flush outbound packets */ } rustls_result result = rustls_connection_ktls_secrets(connection ktls_secrets_callback, &fd); ``` In order to use `rustls_connection_ktls_secrets`, secret extraction must be enabled via `rustls_*_config_builder_set_enable_secret_extraction`.
1 parent cbc8308 commit 359a247

File tree

9 files changed

+261
-3
lines changed

9 files changed

+261
-3
lines changed

Cargo.lock

Lines changed: 111 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

librustls/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repository = "https://github.com/rustls/rustls-ffi"
1010
categories = ["network-programming", "cryptography"]
1111
edition = "2021"
1212
links = "rustls_ffi"
13-
rust-version = "1.71"
13+
rust-version = "1.75"
1414

1515
[features]
1616
default = ["aws-lc-rs", "prefer-post-quantum"]
@@ -27,6 +27,7 @@ aws-lc-rs = ["rustls/aws-lc-rs", "webpki/aws-lc-rs"]
2727
cert_compression = ["rustls/brotli", "rustls/zlib"]
2828
fips = ["rustls/fips", "webpki/aws-lc-rs-fips"]
2929
prefer-post-quantum = ["aws-lc-rs", "rustls/prefer-post-quantum"]
30+
ktls = []
3031

3132
[dependencies]
3233
# Keep in sync with RUSTLS_CRATE_VERSION in build.rs
@@ -36,6 +37,9 @@ libc = { workspace = true }
3637
log = { workspace = true }
3738
rustls-platform-verifier = { workspace = true }
3839

40+
[target.'cfg(target_os = "linux")'.dependencies]
41+
ktls = "6.0.2"
42+
3943
[lib]
4044
name = "rustls_ffi"
4145
crate-type = ["lib", "staticlib"]

librustls/cbindgen.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ include = ["rustls_tls_version"]
4141
"feature = aws-lc-rs" = "DEFINE_AWS_LC_RS"
4242
"feature = ring" = "DEFINE_RING"
4343
"feature = fips" = "DEFINE_FIPS"
44+
"feature = ktls" = "DEFINE_KTLS"
4445

4546
[parse.expand]
4647
crates = ["rustls-ffi"]
47-
features = ["read_buf", "aws-lc-rs", "ring", "fips"]
48+
features = ["read_buf", "aws-lc-rs", "ring", "fips", "ktls"]

librustls/cmake/options.cmake

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ option(
2525
"Whether to enable aws-lc-rs and prefer post-quantum key exchange support"
2626
)
2727

28+
option(KTLS "Whether to enable kTLS")
29+
30+
if (KTLS AND (APPLE OR WIN32))
31+
message(
32+
FATAL_ERROR
33+
"kTLS is not supported with MacOS or Windows"
34+
)
35+
endif()
36+
2837
option(DYN_LINK "Use dynamic linking for rustls library" OFF)
2938

3039
if(DYN_LINK AND FIPS AND (APPLE OR WIN32))
@@ -54,6 +63,10 @@ if(PREFER_POST_QUANTUM)
5463
list(APPEND CARGO_FEATURES --features=prefer-post-quantum)
5564
endif()
5665

66+
if(KTLS)
67+
list(APPEND CARGO_FEATURES --features=ktls)
68+
endif()
69+
5770
# By default w/ Makefile or Ninja generators (e.g. Linux/MacOS CLI)
5871
# the `CMAKE_BUILD_TYPE` is "" when using the C/C++ project tooling.
5972
#

librustls/src/client.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub(crate) struct ClientConfigBuilder {
5454
cert_resolver: Option<Arc<dyn ResolvesClientCert>>,
5555
key_log: Option<Arc<dyn KeyLog>>,
5656
ech_mode: Option<EchMode>,
57+
enable_secret_extraction: bool,
5758
}
5859

5960
impl Default for ClientConfigBuilder {
@@ -72,6 +73,7 @@ impl Default for ClientConfigBuilder {
7273
cert_resolver: None,
7374
key_log: None,
7475
ech_mode: None,
76+
enable_secret_extraction: false,
7577
}
7678
}
7779
}
@@ -272,6 +274,18 @@ impl rustls_client_config_builder {
272274
}
273275
}
274276

277+
/// Enable or disable secret extraction, e.g. for kTLS.
278+
#[no_mangle]
279+
pub extern "C" fn rustls_client_config_builder_set_enable_secret_extraction(
280+
config: *mut rustls_client_config_builder,
281+
enable: bool,
282+
) {
283+
ffi_panic_boundary! {
284+
let config = try_mut_from_ptr!(config);
285+
config.enable_secret_extraction = enable;
286+
}
287+
}
288+
275289
/// Provide the configuration a list of certificates where the connection
276290
/// will select the first one that is compatible with the server's signature
277291
/// verification capabilities.
@@ -529,6 +543,7 @@ impl rustls_client_config_builder {
529543
};
530544
config.alpn_protocols = builder.alpn_protocols;
531545
config.enable_sni = builder.enable_sni;
546+
config.enable_secret_extraction = builder.enable_secret_extraction;
532547

533548
if let Some(key_log) = builder.key_log {
534549
config.key_log = key_log;

librustls/src/connection.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use rustls::{ClientConnection, ServerConnection};
1010
use crate::certificate::rustls_certificate;
1111
use crate::enums::rustls_handshake_kind;
1212
use crate::error::{map_error, rustls_io_result, rustls_result};
13+
#[cfg(feature = "ktls")]
14+
use crate::ffi::try_box_from_ptr;
1315
use crate::ffi::{
1416
box_castable, free_box, try_callback, try_mut_from_ptr, try_ref_from_ptr, try_slice,
1517
try_slice_mut,
@@ -97,6 +99,17 @@ box_castable! {
9799
pub struct rustls_connection(Connection);
98100
}
99101

102+
#[cfg(feature = "ktls")]
103+
pub type rustls_ktls_secrets_callback = Option<
104+
unsafe extern "C" fn(
105+
userdata: *mut c_void,
106+
rx_buf: *const c_void,
107+
rx_n: size_t,
108+
tx_buf: *const c_void,
109+
tx_n: size_t,
110+
) -> rustls_io_result,
111+
>;
112+
100113
impl rustls_connection {
101114
/// Set the userdata pointer associated with this connection. This will be passed
102115
/// to any callbacks invoked by the connection, if you've set up callbacks in the config.
@@ -302,6 +315,47 @@ impl rustls_connection {
302315
}
303316
}
304317

318+
/// Extract secrets and consume connection. Must not be called twice
319+
/// with the same value. Secrets are borrowed in `callback` and should
320+
/// not be retained across calls. The `userdata` parameter is passed
321+
/// through directly to `callback`. Note that this is distinct from the
322+
/// `userdata` parameter set with `rustls_connection_set_userdata`.
323+
#[cfg(feature = "ktls")]
324+
#[no_mangle]
325+
pub extern "C" fn rustls_connection_ktls_secrets(
326+
conn: *mut rustls_connection,
327+
callback: rustls_ktls_secrets_callback,
328+
userdata: *mut c_void,
329+
) -> rustls_result {
330+
ffi_panic_boundary! {
331+
if conn.is_null() {
332+
return rustls_result::NullParameter;
333+
}
334+
let tls = try_box_from_ptr!(conn).conn;
335+
let suite = tls.negotiated_cipher_suite().unwrap();
336+
match tls.dangerous_extract_secrets() {
337+
Ok(extracted_secrets) => {
338+
let Ok(rx) = ktls::CryptoInfo::from_rustls(suite, extracted_secrets.rx) else {
339+
return rustls_result::HandshakeNotComplete;
340+
};
341+
let Ok(tx) = ktls::CryptoInfo::from_rustls(suite, extracted_secrets.tx) else {
342+
return rustls_result::HandshakeNotComplete;
343+
};
344+
let callback = try_callback!(callback);
345+
let result = unsafe {
346+
callback(userdata, rx.as_ptr(), rx.size(), tx.as_ptr(), tx.size())
347+
};
348+
match result.0 {
349+
0 => rustls_result::Ok,
350+
_ => rustls_result::KtlsUserCallback,
351+
}
352+
}
353+
Err(rustls::Error::HandshakeNotComplete) => rustls_result::HandshakeNotComplete,
354+
Err(_) => rustls_result::KtlsSecretExtractionDisabled,
355+
}
356+
}
357+
}
358+
305359
/// Sets a limit on the internal buffers used to buffer unsent plaintext (prior
306360
/// to completing the TLS handshake) and unsent TLS records. By default, there
307361
/// is no limit. The limit can be set at any time, even if the current buffer

librustls/src/error.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,11 @@ u32_enum_builder! {
211211
// From InvalidEncryptedClientHello, with fields that get flattened.
212212
InvalidEncryptedClientHelloInvalidConfigList => 7700,
213213
InvalidEncryptedClientHelloNoCompatibleConfig => 7701,
214-
InvalidEncryptedClientHelloSniRequired => 7702
214+
InvalidEncryptedClientHelloSniRequired => 7702,
215+
216+
KtlsCompatibility => 7800,
217+
KtlsUserCallback => 7801,
218+
KtlsSecretExtractionDisabled => 7802
215219
}
216220
}
217221

@@ -551,6 +555,9 @@ impl Display for rustls_result {
551555
InvalidEncryptedClientHelloSniRequired => {
552556
Error::InvalidEncryptedClientHello(EncryptedClientHelloError::SniRequired).fmt(f)
553557
}
558+
KtlsCompatibility => write!(f, "kTLS compatibility error"),
559+
KtlsUserCallback => write!(f, "kTLS user callback"),
560+
KtlsSecretExtractionDisabled => write!(f, "kTLS secret extraction disabled"),
554561
}
555562
}
556563
}

0 commit comments

Comments
 (0)