├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Justfile ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── ktls-sys ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── Justfile ├── README.md ├── src │ ├── bindings.rs │ └── lib.rs └── tls.h ├── ktls ├── CHANGELOG.md ├── Cargo.toml ├── README.md └── src │ ├── async_read_ready.rs │ ├── cork_stream.rs │ ├── ffi.rs │ ├── ktls_stream.rs │ └── lib.rs └── tests └── integration_test.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | # .editorconfig 2 | [*.yml] 3 | indent_style = space 4 | indent_size = 2 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ["fasterthanlime"] 4 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | merge_group: 8 | 9 | jobs: 10 | test: 11 | name: test 12 | runs-on: ubuntu-latest 13 | env: 14 | RUSTC_WRAPPER: sccache 15 | SCCACHE_GHA_ENABLED: true 16 | CARGO_INCREMENTAL: 0 17 | steps: 18 | - name: Check out repository code 19 | uses: actions/checkout@v4 20 | with: 21 | persist-credentials: false 22 | - uses: dtolnay/rust-toolchain@stable 23 | with: 24 | components: llvm-tools, clippy, rust-src 25 | - name: Run sccache-cache 26 | uses: mozilla-actions/sccache-action@v0.0.9 27 | - uses: taiki-e/install-action@v2 28 | with: 29 | tool: just,cargo-llvm-cov,cargo-nextest 30 | - name: Run tests 31 | run: | 32 | cd ${{ github.workspace }} 33 | just clippy --all-targets 34 | RUST_BACKTRACE=1 just ci-test 35 | - name: Upload coverage information 36 | run: | 37 | curl -Os https://uploader.codecov.io/latest/linux/codecov 38 | chmod +x codecov 39 | ./codecov 40 | 41 | msrv: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v4 45 | with: 46 | persist-credentials: false 47 | - uses: dtolnay/rust-toolchain@master 48 | with: 49 | toolchain: 1.75.0 50 | components: llvm-tools, clippy, rust-src 51 | - name: Run sccache-cache 52 | uses: mozilla-actions/sccache-action@v0.0.9 53 | - uses: taiki-e/install-action@v2 54 | with: 55 | tool: just 56 | - name: Check library code with minimum supported Rust version 57 | run: | 58 | just check --lib --locked 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *coverage* 3 | .direnv 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "autocfg" 31 | version = "1.4.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 34 | 35 | [[package]] 36 | name = "aws-lc-rs" 37 | version = "1.13.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" 40 | dependencies = [ 41 | "aws-lc-sys", 42 | "zeroize", 43 | ] 44 | 45 | [[package]] 46 | name = "aws-lc-sys" 47 | version = "0.28.0" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "b9f7720b74ed28ca77f90769a71fd8c637a0137f6fae4ae947e1050229cff57f" 50 | dependencies = [ 51 | "bindgen", 52 | "cc", 53 | "cmake", 54 | "dunce", 55 | "fs_extra", 56 | ] 57 | 58 | [[package]] 59 | name = "backtrace" 60 | version = "0.3.74" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 63 | dependencies = [ 64 | "addr2line", 65 | "cfg-if", 66 | "libc", 67 | "miniz_oxide", 68 | "object", 69 | "rustc-demangle", 70 | "windows-targets", 71 | ] 72 | 73 | [[package]] 74 | name = "base64" 75 | version = "0.22.1" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 78 | 79 | [[package]] 80 | name = "bindgen" 81 | version = "0.69.5" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" 84 | dependencies = [ 85 | "bitflags", 86 | "cexpr", 87 | "clang-sys", 88 | "itertools", 89 | "lazy_static", 90 | "lazycell", 91 | "log", 92 | "prettyplease", 93 | "proc-macro2", 94 | "quote", 95 | "regex", 96 | "rustc-hash", 97 | "shlex", 98 | "syn", 99 | "which", 100 | ] 101 | 102 | [[package]] 103 | name = "bitflags" 104 | version = "2.9.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" 107 | 108 | [[package]] 109 | name = "bytes" 110 | version = "1.10.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 113 | 114 | [[package]] 115 | name = "cc" 116 | version = "1.2.18" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" 119 | dependencies = [ 120 | "jobserver", 121 | "libc", 122 | "shlex", 123 | ] 124 | 125 | [[package]] 126 | name = "cexpr" 127 | version = "0.6.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" 130 | dependencies = [ 131 | "nom", 132 | ] 133 | 134 | [[package]] 135 | name = "cfg-if" 136 | version = "1.0.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 139 | 140 | [[package]] 141 | name = "cfg_aliases" 142 | version = "0.2.1" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 145 | 146 | [[package]] 147 | name = "clang-sys" 148 | version = "1.8.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" 151 | dependencies = [ 152 | "glob", 153 | "libc", 154 | "libloading", 155 | ] 156 | 157 | [[package]] 158 | name = "cmake" 159 | version = "0.1.54" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" 162 | dependencies = [ 163 | "cc", 164 | ] 165 | 166 | [[package]] 167 | name = "deranged" 168 | version = "0.4.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 171 | dependencies = [ 172 | "powerfmt", 173 | ] 174 | 175 | [[package]] 176 | name = "dunce" 177 | version = "1.0.5" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" 180 | 181 | [[package]] 182 | name = "either" 183 | version = "1.15.0" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 186 | 187 | [[package]] 188 | name = "equivalent" 189 | version = "1.0.2" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 192 | 193 | [[package]] 194 | name = "errno" 195 | version = "0.3.11" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" 198 | dependencies = [ 199 | "libc", 200 | "windows-sys 0.59.0", 201 | ] 202 | 203 | [[package]] 204 | name = "fs_extra" 205 | version = "1.3.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" 208 | 209 | [[package]] 210 | name = "futures-core" 211 | version = "0.3.31" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 214 | 215 | [[package]] 216 | name = "futures-macro" 217 | version = "0.3.31" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 220 | dependencies = [ 221 | "proc-macro2", 222 | "quote", 223 | "syn", 224 | ] 225 | 226 | [[package]] 227 | name = "futures-task" 228 | version = "0.3.31" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 231 | 232 | [[package]] 233 | name = "futures-util" 234 | version = "0.3.31" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 237 | dependencies = [ 238 | "futures-core", 239 | "futures-macro", 240 | "futures-task", 241 | "pin-project-lite", 242 | "pin-utils", 243 | "slab", 244 | ] 245 | 246 | [[package]] 247 | name = "getrandom" 248 | version = "0.2.15" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 251 | dependencies = [ 252 | "cfg-if", 253 | "libc", 254 | "wasi 0.11.0+wasi-snapshot-preview1", 255 | ] 256 | 257 | [[package]] 258 | name = "getrandom" 259 | version = "0.3.2" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" 262 | dependencies = [ 263 | "cfg-if", 264 | "libc", 265 | "r-efi", 266 | "wasi 0.14.2+wasi-0.2.4", 267 | ] 268 | 269 | [[package]] 270 | name = "gimli" 271 | version = "0.31.1" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 274 | 275 | [[package]] 276 | name = "glob" 277 | version = "0.3.2" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" 280 | 281 | [[package]] 282 | name = "hashbrown" 283 | version = "0.15.2" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 286 | 287 | [[package]] 288 | name = "home" 289 | version = "0.5.11" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" 292 | dependencies = [ 293 | "windows-sys 0.59.0", 294 | ] 295 | 296 | [[package]] 297 | name = "indexmap" 298 | version = "2.9.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 301 | dependencies = [ 302 | "equivalent", 303 | "hashbrown", 304 | ] 305 | 306 | [[package]] 307 | name = "itertools" 308 | version = "0.12.1" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 311 | dependencies = [ 312 | "either", 313 | ] 314 | 315 | [[package]] 316 | name = "jobserver" 317 | version = "0.1.33" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" 320 | dependencies = [ 321 | "getrandom 0.3.2", 322 | "libc", 323 | ] 324 | 325 | [[package]] 326 | name = "ktls" 327 | version = "6.0.2" 328 | dependencies = [ 329 | "futures-util", 330 | "ktls-sys 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 331 | "lazy_static", 332 | "libc", 333 | "memoffset", 334 | "nix", 335 | "num_enum", 336 | "oorandom", 337 | "pin-project-lite", 338 | "rcgen", 339 | "rustls", 340 | "smallvec", 341 | "socket2", 342 | "test-case", 343 | "thiserror", 344 | "tokio", 345 | "tokio-rustls", 346 | "tracing", 347 | "tracing-subscriber", 348 | ] 349 | 350 | [[package]] 351 | name = "ktls-sys" 352 | version = "1.0.2" 353 | 354 | [[package]] 355 | name = "ktls-sys" 356 | version = "1.0.2" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "5ed84c81d133bc00e291085cff51c15cb07d3cede0aade1f2d82dd33df82d7e7" 359 | 360 | [[package]] 361 | name = "lazy_static" 362 | version = "1.5.0" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 365 | 366 | [[package]] 367 | name = "lazycell" 368 | version = "1.3.0" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 371 | 372 | [[package]] 373 | name = "libc" 374 | version = "0.2.171" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 377 | 378 | [[package]] 379 | name = "libloading" 380 | version = "0.8.6" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" 383 | dependencies = [ 384 | "cfg-if", 385 | "windows-targets", 386 | ] 387 | 388 | [[package]] 389 | name = "linux-raw-sys" 390 | version = "0.4.15" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 393 | 394 | [[package]] 395 | name = "lock_api" 396 | version = "0.4.12" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 399 | dependencies = [ 400 | "autocfg", 401 | "scopeguard", 402 | ] 403 | 404 | [[package]] 405 | name = "log" 406 | version = "0.4.27" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 409 | 410 | [[package]] 411 | name = "matchers" 412 | version = "0.1.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 415 | dependencies = [ 416 | "regex-automata 0.1.10", 417 | ] 418 | 419 | [[package]] 420 | name = "memchr" 421 | version = "2.7.4" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 424 | 425 | [[package]] 426 | name = "memoffset" 427 | version = "0.9.1" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 430 | dependencies = [ 431 | "autocfg", 432 | ] 433 | 434 | [[package]] 435 | name = "minimal-lexical" 436 | version = "0.2.1" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 439 | 440 | [[package]] 441 | name = "miniz_oxide" 442 | version = "0.8.7" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" 445 | dependencies = [ 446 | "adler2", 447 | ] 448 | 449 | [[package]] 450 | name = "mio" 451 | version = "1.0.3" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 454 | dependencies = [ 455 | "libc", 456 | "wasi 0.11.0+wasi-snapshot-preview1", 457 | "windows-sys 0.52.0", 458 | ] 459 | 460 | [[package]] 461 | name = "nix" 462 | version = "0.29.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" 465 | dependencies = [ 466 | "bitflags", 467 | "cfg-if", 468 | "cfg_aliases", 469 | "libc", 470 | "memoffset", 471 | ] 472 | 473 | [[package]] 474 | name = "nom" 475 | version = "7.1.3" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 478 | dependencies = [ 479 | "memchr", 480 | "minimal-lexical", 481 | ] 482 | 483 | [[package]] 484 | name = "nu-ansi-term" 485 | version = "0.46.0" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 488 | dependencies = [ 489 | "overload", 490 | "winapi", 491 | ] 492 | 493 | [[package]] 494 | name = "num-conv" 495 | version = "0.1.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 498 | 499 | [[package]] 500 | name = "num_enum" 501 | version = "0.7.3" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" 504 | dependencies = [ 505 | "num_enum_derive", 506 | ] 507 | 508 | [[package]] 509 | name = "num_enum_derive" 510 | version = "0.7.3" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" 513 | dependencies = [ 514 | "proc-macro-crate", 515 | "proc-macro2", 516 | "quote", 517 | "syn", 518 | ] 519 | 520 | [[package]] 521 | name = "object" 522 | version = "0.36.7" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 525 | dependencies = [ 526 | "memchr", 527 | ] 528 | 529 | [[package]] 530 | name = "once_cell" 531 | version = "1.21.3" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 534 | 535 | [[package]] 536 | name = "oorandom" 537 | version = "11.1.5" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" 540 | 541 | [[package]] 542 | name = "overload" 543 | version = "0.1.1" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 546 | 547 | [[package]] 548 | name = "parking_lot" 549 | version = "0.12.3" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 552 | dependencies = [ 553 | "lock_api", 554 | "parking_lot_core", 555 | ] 556 | 557 | [[package]] 558 | name = "parking_lot_core" 559 | version = "0.9.10" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 562 | dependencies = [ 563 | "cfg-if", 564 | "libc", 565 | "redox_syscall", 566 | "smallvec", 567 | "windows-targets", 568 | ] 569 | 570 | [[package]] 571 | name = "pem" 572 | version = "3.0.5" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" 575 | dependencies = [ 576 | "base64", 577 | "serde", 578 | ] 579 | 580 | [[package]] 581 | name = "pin-project-lite" 582 | version = "0.2.16" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 585 | 586 | [[package]] 587 | name = "pin-utils" 588 | version = "0.1.0" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 591 | 592 | [[package]] 593 | name = "powerfmt" 594 | version = "0.2.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 597 | 598 | [[package]] 599 | name = "prettyplease" 600 | version = "0.2.32" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" 603 | dependencies = [ 604 | "proc-macro2", 605 | "syn", 606 | ] 607 | 608 | [[package]] 609 | name = "proc-macro-crate" 610 | version = "3.3.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" 613 | dependencies = [ 614 | "toml_edit", 615 | ] 616 | 617 | [[package]] 618 | name = "proc-macro2" 619 | version = "1.0.94" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 622 | dependencies = [ 623 | "unicode-ident", 624 | ] 625 | 626 | [[package]] 627 | name = "quote" 628 | version = "1.0.40" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 631 | dependencies = [ 632 | "proc-macro2", 633 | ] 634 | 635 | [[package]] 636 | name = "r-efi" 637 | version = "5.2.0" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 640 | 641 | [[package]] 642 | name = "rcgen" 643 | version = "0.13.2" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" 646 | dependencies = [ 647 | "pem", 648 | "ring", 649 | "rustls-pki-types", 650 | "time", 651 | "yasna", 652 | ] 653 | 654 | [[package]] 655 | name = "redox_syscall" 656 | version = "0.5.11" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" 659 | dependencies = [ 660 | "bitflags", 661 | ] 662 | 663 | [[package]] 664 | name = "regex" 665 | version = "1.11.1" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 668 | dependencies = [ 669 | "aho-corasick", 670 | "memchr", 671 | "regex-automata 0.4.9", 672 | "regex-syntax 0.8.5", 673 | ] 674 | 675 | [[package]] 676 | name = "regex-automata" 677 | version = "0.1.10" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 680 | dependencies = [ 681 | "regex-syntax 0.6.29", 682 | ] 683 | 684 | [[package]] 685 | name = "regex-automata" 686 | version = "0.4.9" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 689 | dependencies = [ 690 | "aho-corasick", 691 | "memchr", 692 | "regex-syntax 0.8.5", 693 | ] 694 | 695 | [[package]] 696 | name = "regex-syntax" 697 | version = "0.6.29" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 700 | 701 | [[package]] 702 | name = "regex-syntax" 703 | version = "0.8.5" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 706 | 707 | [[package]] 708 | name = "ring" 709 | version = "0.17.14" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 712 | dependencies = [ 713 | "cc", 714 | "cfg-if", 715 | "getrandom 0.2.15", 716 | "libc", 717 | "untrusted", 718 | "windows-sys 0.52.0", 719 | ] 720 | 721 | [[package]] 722 | name = "rustc-demangle" 723 | version = "0.1.24" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 726 | 727 | [[package]] 728 | name = "rustc-hash" 729 | version = "1.1.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 732 | 733 | [[package]] 734 | name = "rustix" 735 | version = "0.38.44" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 738 | dependencies = [ 739 | "bitflags", 740 | "errno", 741 | "libc", 742 | "linux-raw-sys", 743 | "windows-sys 0.59.0", 744 | ] 745 | 746 | [[package]] 747 | name = "rustls" 748 | version = "0.23.25" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" 751 | dependencies = [ 752 | "aws-lc-rs", 753 | "once_cell", 754 | "ring", 755 | "rustls-pki-types", 756 | "rustls-webpki", 757 | "subtle", 758 | "zeroize", 759 | ] 760 | 761 | [[package]] 762 | name = "rustls-pki-types" 763 | version = "1.11.0" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" 766 | 767 | [[package]] 768 | name = "rustls-webpki" 769 | version = "0.103.1" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" 772 | dependencies = [ 773 | "aws-lc-rs", 774 | "ring", 775 | "rustls-pki-types", 776 | "untrusted", 777 | ] 778 | 779 | [[package]] 780 | name = "scopeguard" 781 | version = "1.2.0" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 784 | 785 | [[package]] 786 | name = "serde" 787 | version = "1.0.219" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 790 | dependencies = [ 791 | "serde_derive", 792 | ] 793 | 794 | [[package]] 795 | name = "serde_derive" 796 | version = "1.0.219" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 799 | dependencies = [ 800 | "proc-macro2", 801 | "quote", 802 | "syn", 803 | ] 804 | 805 | [[package]] 806 | name = "sharded-slab" 807 | version = "0.1.7" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 810 | dependencies = [ 811 | "lazy_static", 812 | ] 813 | 814 | [[package]] 815 | name = "shlex" 816 | version = "1.3.0" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 819 | 820 | [[package]] 821 | name = "signal-hook-registry" 822 | version = "1.4.2" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 825 | dependencies = [ 826 | "libc", 827 | ] 828 | 829 | [[package]] 830 | name = "slab" 831 | version = "0.4.9" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 834 | dependencies = [ 835 | "autocfg", 836 | ] 837 | 838 | [[package]] 839 | name = "smallvec" 840 | version = "1.15.0" 841 | source = "registry+https://github.com/rust-lang/crates.io-index" 842 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" 843 | 844 | [[package]] 845 | name = "socket2" 846 | version = "0.5.9" 847 | source = "registry+https://github.com/rust-lang/crates.io-index" 848 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" 849 | dependencies = [ 850 | "libc", 851 | "windows-sys 0.52.0", 852 | ] 853 | 854 | [[package]] 855 | name = "subtle" 856 | version = "2.6.1" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 859 | 860 | [[package]] 861 | name = "syn" 862 | version = "2.0.100" 863 | source = "registry+https://github.com/rust-lang/crates.io-index" 864 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 865 | dependencies = [ 866 | "proc-macro2", 867 | "quote", 868 | "unicode-ident", 869 | ] 870 | 871 | [[package]] 872 | name = "test-case" 873 | version = "3.3.1" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" 876 | dependencies = [ 877 | "test-case-macros", 878 | ] 879 | 880 | [[package]] 881 | name = "test-case-core" 882 | version = "3.3.1" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" 885 | dependencies = [ 886 | "cfg-if", 887 | "proc-macro2", 888 | "quote", 889 | "syn", 890 | ] 891 | 892 | [[package]] 893 | name = "test-case-macros" 894 | version = "3.3.1" 895 | source = "registry+https://github.com/rust-lang/crates.io-index" 896 | checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" 897 | dependencies = [ 898 | "proc-macro2", 899 | "quote", 900 | "syn", 901 | "test-case-core", 902 | ] 903 | 904 | [[package]] 905 | name = "thiserror" 906 | version = "2.0.12" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 909 | dependencies = [ 910 | "thiserror-impl", 911 | ] 912 | 913 | [[package]] 914 | name = "thiserror-impl" 915 | version = "2.0.12" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 918 | dependencies = [ 919 | "proc-macro2", 920 | "quote", 921 | "syn", 922 | ] 923 | 924 | [[package]] 925 | name = "thread_local" 926 | version = "1.1.8" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 929 | dependencies = [ 930 | "cfg-if", 931 | "once_cell", 932 | ] 933 | 934 | [[package]] 935 | name = "time" 936 | version = "0.3.41" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 939 | dependencies = [ 940 | "deranged", 941 | "num-conv", 942 | "powerfmt", 943 | "serde", 944 | "time-core", 945 | ] 946 | 947 | [[package]] 948 | name = "time-core" 949 | version = "0.1.4" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 952 | 953 | [[package]] 954 | name = "tokio" 955 | version = "1.44.2" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" 958 | dependencies = [ 959 | "backtrace", 960 | "bytes", 961 | "libc", 962 | "mio", 963 | "parking_lot", 964 | "pin-project-lite", 965 | "signal-hook-registry", 966 | "socket2", 967 | "tokio-macros", 968 | "windows-sys 0.52.0", 969 | ] 970 | 971 | [[package]] 972 | name = "tokio-macros" 973 | version = "2.5.0" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 976 | dependencies = [ 977 | "proc-macro2", 978 | "quote", 979 | "syn", 980 | ] 981 | 982 | [[package]] 983 | name = "tokio-rustls" 984 | version = "0.26.2" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 987 | dependencies = [ 988 | "rustls", 989 | "tokio", 990 | ] 991 | 992 | [[package]] 993 | name = "toml_datetime" 994 | version = "0.6.8" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 997 | 998 | [[package]] 999 | name = "toml_edit" 1000 | version = "0.22.24" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" 1003 | dependencies = [ 1004 | "indexmap", 1005 | "toml_datetime", 1006 | "winnow", 1007 | ] 1008 | 1009 | [[package]] 1010 | name = "tracing" 1011 | version = "0.1.41" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1014 | dependencies = [ 1015 | "pin-project-lite", 1016 | "tracing-attributes", 1017 | "tracing-core", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "tracing-attributes" 1022 | version = "0.1.28" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 1025 | dependencies = [ 1026 | "proc-macro2", 1027 | "quote", 1028 | "syn", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "tracing-core" 1033 | version = "0.1.33" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 1036 | dependencies = [ 1037 | "once_cell", 1038 | "valuable", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "tracing-log" 1043 | version = "0.2.0" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1046 | dependencies = [ 1047 | "log", 1048 | "once_cell", 1049 | "tracing-core", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "tracing-subscriber" 1054 | version = "0.3.19" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 1057 | dependencies = [ 1058 | "matchers", 1059 | "nu-ansi-term", 1060 | "once_cell", 1061 | "regex", 1062 | "sharded-slab", 1063 | "smallvec", 1064 | "thread_local", 1065 | "tracing", 1066 | "tracing-core", 1067 | "tracing-log", 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "unicode-ident" 1072 | version = "1.0.18" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1075 | 1076 | [[package]] 1077 | name = "untrusted" 1078 | version = "0.9.0" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1081 | 1082 | [[package]] 1083 | name = "valuable" 1084 | version = "0.1.1" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 1087 | 1088 | [[package]] 1089 | name = "wasi" 1090 | version = "0.11.0+wasi-snapshot-preview1" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1093 | 1094 | [[package]] 1095 | name = "wasi" 1096 | version = "0.14.2+wasi-0.2.4" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1099 | dependencies = [ 1100 | "wit-bindgen-rt", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "which" 1105 | version = "4.4.2" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" 1108 | dependencies = [ 1109 | "either", 1110 | "home", 1111 | "once_cell", 1112 | "rustix", 1113 | ] 1114 | 1115 | [[package]] 1116 | name = "winapi" 1117 | version = "0.3.9" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1120 | dependencies = [ 1121 | "winapi-i686-pc-windows-gnu", 1122 | "winapi-x86_64-pc-windows-gnu", 1123 | ] 1124 | 1125 | [[package]] 1126 | name = "winapi-i686-pc-windows-gnu" 1127 | version = "0.4.0" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1130 | 1131 | [[package]] 1132 | name = "winapi-x86_64-pc-windows-gnu" 1133 | version = "0.4.0" 1134 | source = "registry+https://github.com/rust-lang/crates.io-index" 1135 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1136 | 1137 | [[package]] 1138 | name = "windows-sys" 1139 | version = "0.52.0" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1142 | dependencies = [ 1143 | "windows-targets", 1144 | ] 1145 | 1146 | [[package]] 1147 | name = "windows-sys" 1148 | version = "0.59.0" 1149 | source = "registry+https://github.com/rust-lang/crates.io-index" 1150 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1151 | dependencies = [ 1152 | "windows-targets", 1153 | ] 1154 | 1155 | [[package]] 1156 | name = "windows-targets" 1157 | version = "0.52.6" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1160 | dependencies = [ 1161 | "windows_aarch64_gnullvm", 1162 | "windows_aarch64_msvc", 1163 | "windows_i686_gnu", 1164 | "windows_i686_gnullvm", 1165 | "windows_i686_msvc", 1166 | "windows_x86_64_gnu", 1167 | "windows_x86_64_gnullvm", 1168 | "windows_x86_64_msvc", 1169 | ] 1170 | 1171 | [[package]] 1172 | name = "windows_aarch64_gnullvm" 1173 | version = "0.52.6" 1174 | source = "registry+https://github.com/rust-lang/crates.io-index" 1175 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1176 | 1177 | [[package]] 1178 | name = "windows_aarch64_msvc" 1179 | version = "0.52.6" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1182 | 1183 | [[package]] 1184 | name = "windows_i686_gnu" 1185 | version = "0.52.6" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1188 | 1189 | [[package]] 1190 | name = "windows_i686_gnullvm" 1191 | version = "0.52.6" 1192 | source = "registry+https://github.com/rust-lang/crates.io-index" 1193 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1194 | 1195 | [[package]] 1196 | name = "windows_i686_msvc" 1197 | version = "0.52.6" 1198 | source = "registry+https://github.com/rust-lang/crates.io-index" 1199 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1200 | 1201 | [[package]] 1202 | name = "windows_x86_64_gnu" 1203 | version = "0.52.6" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1206 | 1207 | [[package]] 1208 | name = "windows_x86_64_gnullvm" 1209 | version = "0.52.6" 1210 | source = "registry+https://github.com/rust-lang/crates.io-index" 1211 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1212 | 1213 | [[package]] 1214 | name = "windows_x86_64_msvc" 1215 | version = "0.52.6" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1218 | 1219 | [[package]] 1220 | name = "winnow" 1221 | version = "0.7.6" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" 1224 | dependencies = [ 1225 | "memchr", 1226 | ] 1227 | 1228 | [[package]] 1229 | name = "wit-bindgen-rt" 1230 | version = "0.39.0" 1231 | source = "registry+https://github.com/rust-lang/crates.io-index" 1232 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 1233 | dependencies = [ 1234 | "bitflags", 1235 | ] 1236 | 1237 | [[package]] 1238 | name = "yasna" 1239 | version = "0.5.2" 1240 | source = "registry+https://github.com/rust-lang/crates.io-index" 1241 | checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" 1242 | dependencies = [ 1243 | "time", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "zeroize" 1248 | version = "1.8.1" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 1251 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["ktls", "ktls-sys"] 3 | resolver = "2" 4 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | # just manual: https://github.com/casey/just#readme 2 | 3 | _default: 4 | just --list 5 | 6 | # Run all tests with nextest and cargo-llvm-cov 7 | ci-test: 8 | #!/bin/bash -eux 9 | for backend in ring aws_lc_rs; do 10 | cargo llvm-cov nextest --no-default-features --features tls12,$backend --lcov --output-path coverage.lcov 11 | done 12 | 13 | # Show coverage locally 14 | cov: 15 | #!/bin/bash -eux 16 | cargo llvm-cov nextest --hide-instantiations --html --output-dir coverage 17 | 18 | t *args: 19 | just test {{args}} 20 | 21 | # Run all tests 22 | test *args: 23 | #!/bin/bash -eux 24 | export RUST_BACKTRACE=1 25 | for feature in ring aws_lc_rs; do 26 | cargo nextest run --no-default-features --features tls12,$feature {{args}} 27 | done 28 | 29 | c *args: 30 | just check {{args}} 31 | 32 | clippy *args: 33 | cargo clippy {{args}} --no-default-features --features tls12,ring 34 | cargo clippy {{args}} --no-default-features --features tls12,aws_lc_rs 35 | 36 | check *args: 37 | cargo check {{args}} --no-default-features --features tls12,ring 38 | cargo check {{args}} --no-default-features --features tls12,aws_lc_rs 39 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | https://www.apache.org/licenses/LICENSE-2.0 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | https://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![test pipeline](https://github.com/hapsoc/ktls/actions/workflows/test.yml/badge.svg)](https://github.com/hapsoc/ktls/actions/workflows/test.yml?query=branch%3Amain) 2 | [![Coverage Status (codecov.io)](https://codecov.io/gh/hapsoc/ktls/branch/main/graph/badge.svg)](https://codecov.io/gh/hapsoc/ktls/) 3 | [![license: MIT/Apache-2.0](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE-MIT) 4 | 5 | # ktls 6 | 7 | This repository hosts both: 8 | 9 | * [ktls](./ktls): higher-level, safe wrappers over kTLS 10 | * [ktls-sys](./ktls-sys): the raw system interface for kTLS on Linux 11 | 12 | ## License 13 | 14 | This project is primarily distributed under the terms of both the MIT license 15 | and the Apache License (Version 2.0). 16 | 17 | See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. 18 | -------------------------------------------------------------------------------- /ktls-sys/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [1.0.2](https://github.com/rustls/ktls/compare/ktls-sys-v1.0.1...ktls-sys-v1.0.2) - 2024-09-26 10 | 11 | ### Other 12 | 13 | - Standardize READMEs a little 14 | - Turn ktls-sys into a workspace 15 | 16 | ## [1.0.1](https://github.com/bearcove/ktls-sys/compare/v1.0.0...v1.0.1) - 2024-03-20 17 | 18 | ### Other 19 | - Configure release-plz 20 | - hapsoc => bearcove 21 | - Ignore .DS_Store files 22 | - Switch to GitHub-hosted runners 23 | - bump toolchain (for real) 24 | - Bump toolchain 25 | -------------------------------------------------------------------------------- /ktls-sys/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ktls-sys" 7 | version = "1.0.1" 8 | -------------------------------------------------------------------------------- /ktls-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ktls-sys" 3 | version = "1.0.2" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | repository = "https://github.com/rustls/ktls-sys" 7 | documentation = "https://docs.rs/ktls-sys" 8 | authors = ["Amos Wenger "] 9 | readme = "README.md" 10 | description = """ 11 | FFI bindings for `linux/tls.h` 12 | """ 13 | rust-version = "1.75" 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /ktls-sys/Justfile: -------------------------------------------------------------------------------- 1 | # just manual: https://github.com/casey/just#readme 2 | 3 | _default: 4 | just --list 5 | 6 | # Run all tests with nextest and cargo-llvm-cov 7 | ci-test: 8 | #!/bin/bash -eux 9 | cargo llvm-cov nextest --lcov --output-path coverage.lcov 10 | 11 | # Run all tests with cargo nextest 12 | test *args: 13 | RUST_BACKTRACE=1 cargo nextest run {{args}} 14 | 15 | check: 16 | cargo clippy --all-targets 17 | -------------------------------------------------------------------------------- /ktls-sys/README.md: -------------------------------------------------------------------------------- 1 | [![test pipeline](https://github.com/hapsoc/ktls/actions/workflows/test.yml/badge.svg)](https://github.com/hapsoc/ktls/actions/workflows/test.yml?query=branch%3Amain) 2 | [![Coverage Status (codecov.io)](https://codecov.io/gh/hapsoc/ktls/branch/main/graph/badge.svg)](https://codecov.io/gh/hapsoc/ktls/) 3 | [![Crates.io](https://img.shields.io/crates/v/ktls-sys)](https://crates.io/crates/ktls-sys) 4 | [![license: MIT/Apache-2.0](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE-MIT) 5 | 6 | # ktls-sys 7 | 8 | `linux/tls.h` bindings, for TLS kernel offload. 9 | 10 | Generated with `bindgen tls.h -o src/bindings.rs` 11 | 12 | See for a higher-level / safer interface. 13 | 14 | ## License 15 | 16 | This project is primarily distributed under the terms of both the MIT license 17 | and the Apache License (Version 2.0). 18 | 19 | See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. 20 | -------------------------------------------------------------------------------- /ktls-sys/src/bindings.rs: -------------------------------------------------------------------------------- 1 | /* automatically generated by rust-bindgen 0.60.1 */ 2 | 3 | #[repr(C)] 4 | #[derive(Default)] 5 | pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); 6 | impl __IncompleteArrayField { 7 | #[inline] 8 | pub const fn new() -> Self { 9 | __IncompleteArrayField(::std::marker::PhantomData, []) 10 | } 11 | #[inline] 12 | pub fn as_ptr(&self) -> *const T { 13 | self as *const _ as *const T 14 | } 15 | #[inline] 16 | pub fn as_mut_ptr(&mut self) -> *mut T { 17 | self as *mut _ as *mut T 18 | } 19 | #[inline] 20 | pub unsafe fn as_slice(&self, len: usize) -> &[T] { 21 | ::std::slice::from_raw_parts(self.as_ptr(), len) 22 | } 23 | #[inline] 24 | pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { 25 | ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) 26 | } 27 | } 28 | impl ::std::fmt::Debug for __IncompleteArrayField { 29 | fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { 30 | fmt.write_str("__IncompleteArrayField") 31 | } 32 | } 33 | pub const __BITS_PER_LONG: u32 = 64; 34 | pub const __FD_SETSIZE: u32 = 1024; 35 | pub const TLS_TX: u32 = 1; 36 | pub const TLS_RX: u32 = 2; 37 | pub const TLS_TX_ZEROCOPY_RO: u32 = 3; 38 | pub const TLS_RX_EXPECT_NO_PAD: u32 = 4; 39 | pub const TLS_1_2_VERSION_MAJOR: u32 = 3; 40 | pub const TLS_1_2_VERSION_MINOR: u32 = 3; 41 | pub const TLS_1_3_VERSION_MAJOR: u32 = 3; 42 | pub const TLS_1_3_VERSION_MINOR: u32 = 4; 43 | pub const TLS_CIPHER_AES_GCM_128: u32 = 51; 44 | pub const TLS_CIPHER_AES_GCM_128_IV_SIZE: u32 = 8; 45 | pub const TLS_CIPHER_AES_GCM_128_KEY_SIZE: u32 = 16; 46 | pub const TLS_CIPHER_AES_GCM_128_SALT_SIZE: u32 = 4; 47 | pub const TLS_CIPHER_AES_GCM_128_TAG_SIZE: u32 = 16; 48 | pub const TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE: u32 = 8; 49 | pub const TLS_CIPHER_AES_GCM_256: u32 = 52; 50 | pub const TLS_CIPHER_AES_GCM_256_IV_SIZE: u32 = 8; 51 | pub const TLS_CIPHER_AES_GCM_256_KEY_SIZE: u32 = 32; 52 | pub const TLS_CIPHER_AES_GCM_256_SALT_SIZE: u32 = 4; 53 | pub const TLS_CIPHER_AES_GCM_256_TAG_SIZE: u32 = 16; 54 | pub const TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE: u32 = 8; 55 | pub const TLS_CIPHER_AES_CCM_128: u32 = 53; 56 | pub const TLS_CIPHER_AES_CCM_128_IV_SIZE: u32 = 8; 57 | pub const TLS_CIPHER_AES_CCM_128_KEY_SIZE: u32 = 16; 58 | pub const TLS_CIPHER_AES_CCM_128_SALT_SIZE: u32 = 4; 59 | pub const TLS_CIPHER_AES_CCM_128_TAG_SIZE: u32 = 16; 60 | pub const TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE: u32 = 8; 61 | pub const TLS_CIPHER_CHACHA20_POLY1305: u32 = 54; 62 | pub const TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE: u32 = 12; 63 | pub const TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE: u32 = 32; 64 | pub const TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE: u32 = 0; 65 | pub const TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE: u32 = 16; 66 | pub const TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE: u32 = 8; 67 | pub const TLS_CIPHER_SM4_GCM: u32 = 55; 68 | pub const TLS_CIPHER_SM4_GCM_IV_SIZE: u32 = 8; 69 | pub const TLS_CIPHER_SM4_GCM_KEY_SIZE: u32 = 16; 70 | pub const TLS_CIPHER_SM4_GCM_SALT_SIZE: u32 = 4; 71 | pub const TLS_CIPHER_SM4_GCM_TAG_SIZE: u32 = 16; 72 | pub const TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE: u32 = 8; 73 | pub const TLS_CIPHER_SM4_CCM: u32 = 56; 74 | pub const TLS_CIPHER_SM4_CCM_IV_SIZE: u32 = 8; 75 | pub const TLS_CIPHER_SM4_CCM_KEY_SIZE: u32 = 16; 76 | pub const TLS_CIPHER_SM4_CCM_SALT_SIZE: u32 = 4; 77 | pub const TLS_CIPHER_SM4_CCM_TAG_SIZE: u32 = 16; 78 | pub const TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE: u32 = 8; 79 | pub const TLS_SET_RECORD_TYPE: u32 = 1; 80 | pub const TLS_GET_RECORD_TYPE: u32 = 2; 81 | pub const TLS_CONF_BASE: u32 = 1; 82 | pub const TLS_CONF_SW: u32 = 2; 83 | pub const TLS_CONF_HW: u32 = 3; 84 | pub const TLS_CONF_HW_RECORD: u32 = 4; 85 | pub type __s8 = ::std::os::raw::c_schar; 86 | pub type __u8 = ::std::os::raw::c_uchar; 87 | pub type __s16 = ::std::os::raw::c_short; 88 | pub type __u16 = ::std::os::raw::c_ushort; 89 | pub type __s32 = ::std::os::raw::c_int; 90 | pub type __u32 = ::std::os::raw::c_uint; 91 | pub type __s64 = ::std::os::raw::c_longlong; 92 | pub type __u64 = ::std::os::raw::c_ulonglong; 93 | #[repr(C)] 94 | #[derive(Debug, Copy, Clone)] 95 | pub struct __kernel_fd_set { 96 | pub fds_bits: [::std::os::raw::c_ulong; 16usize], 97 | } 98 | #[test] 99 | fn bindgen_test_layout___kernel_fd_set() { 100 | assert_eq!( 101 | ::std::mem::size_of::<__kernel_fd_set>(), 102 | 128usize, 103 | concat!("Size of: ", stringify!(__kernel_fd_set)) 104 | ); 105 | assert_eq!( 106 | ::std::mem::align_of::<__kernel_fd_set>(), 107 | 8usize, 108 | concat!("Alignment of ", stringify!(__kernel_fd_set)) 109 | ); 110 | fn test_field_fds_bits() { 111 | assert_eq!( 112 | unsafe { 113 | let uninit = ::std::mem::MaybeUninit::<__kernel_fd_set>::uninit(); 114 | let ptr = uninit.as_ptr(); 115 | ::std::ptr::addr_of!((*ptr).fds_bits) as usize - ptr as usize 116 | }, 117 | 0usize, 118 | concat!( 119 | "Offset of field: ", 120 | stringify!(__kernel_fd_set), 121 | "::", 122 | stringify!(fds_bits) 123 | ) 124 | ); 125 | } 126 | test_field_fds_bits(); 127 | } 128 | pub type __kernel_sighandler_t = 129 | ::std::option::Option; 130 | pub type __kernel_key_t = ::std::os::raw::c_int; 131 | pub type __kernel_mqd_t = ::std::os::raw::c_int; 132 | pub type __kernel_old_uid_t = ::std::os::raw::c_ushort; 133 | pub type __kernel_old_gid_t = ::std::os::raw::c_ushort; 134 | pub type __kernel_old_dev_t = ::std::os::raw::c_ulong; 135 | pub type __kernel_long_t = ::std::os::raw::c_long; 136 | pub type __kernel_ulong_t = ::std::os::raw::c_ulong; 137 | pub type __kernel_ino_t = __kernel_ulong_t; 138 | pub type __kernel_mode_t = ::std::os::raw::c_uint; 139 | pub type __kernel_pid_t = ::std::os::raw::c_int; 140 | pub type __kernel_ipc_pid_t = ::std::os::raw::c_int; 141 | pub type __kernel_uid_t = ::std::os::raw::c_uint; 142 | pub type __kernel_gid_t = ::std::os::raw::c_uint; 143 | pub type __kernel_suseconds_t = __kernel_long_t; 144 | pub type __kernel_daddr_t = ::std::os::raw::c_int; 145 | pub type __kernel_uid32_t = ::std::os::raw::c_uint; 146 | pub type __kernel_gid32_t = ::std::os::raw::c_uint; 147 | pub type __kernel_size_t = __kernel_ulong_t; 148 | pub type __kernel_ssize_t = __kernel_long_t; 149 | pub type __kernel_ptrdiff_t = __kernel_long_t; 150 | #[repr(C)] 151 | #[derive(Debug, Copy, Clone)] 152 | pub struct __kernel_fsid_t { 153 | pub val: [::std::os::raw::c_int; 2usize], 154 | } 155 | #[test] 156 | fn bindgen_test_layout___kernel_fsid_t() { 157 | assert_eq!( 158 | ::std::mem::size_of::<__kernel_fsid_t>(), 159 | 8usize, 160 | concat!("Size of: ", stringify!(__kernel_fsid_t)) 161 | ); 162 | assert_eq!( 163 | ::std::mem::align_of::<__kernel_fsid_t>(), 164 | 4usize, 165 | concat!("Alignment of ", stringify!(__kernel_fsid_t)) 166 | ); 167 | fn test_field_val() { 168 | assert_eq!( 169 | unsafe { 170 | let uninit = ::std::mem::MaybeUninit::<__kernel_fsid_t>::uninit(); 171 | let ptr = uninit.as_ptr(); 172 | ::std::ptr::addr_of!((*ptr).val) as usize - ptr as usize 173 | }, 174 | 0usize, 175 | concat!( 176 | "Offset of field: ", 177 | stringify!(__kernel_fsid_t), 178 | "::", 179 | stringify!(val) 180 | ) 181 | ); 182 | } 183 | test_field_val(); 184 | } 185 | pub type __kernel_off_t = __kernel_long_t; 186 | pub type __kernel_loff_t = ::std::os::raw::c_longlong; 187 | pub type __kernel_old_time_t = __kernel_long_t; 188 | pub type __kernel_time_t = __kernel_long_t; 189 | pub type __kernel_time64_t = ::std::os::raw::c_longlong; 190 | pub type __kernel_clock_t = __kernel_long_t; 191 | pub type __kernel_timer_t = ::std::os::raw::c_int; 192 | pub type __kernel_clockid_t = ::std::os::raw::c_int; 193 | pub type __kernel_caddr_t = *mut ::std::os::raw::c_char; 194 | pub type __kernel_uid16_t = ::std::os::raw::c_ushort; 195 | pub type __kernel_gid16_t = ::std::os::raw::c_ushort; 196 | pub type __le16 = __u16; 197 | pub type __be16 = __u16; 198 | pub type __le32 = __u32; 199 | pub type __be32 = __u32; 200 | pub type __le64 = __u64; 201 | pub type __be64 = __u64; 202 | pub type __sum16 = __u16; 203 | pub type __wsum = __u32; 204 | pub type __poll_t = ::std::os::raw::c_uint; 205 | #[repr(C)] 206 | #[derive(Debug, Copy, Clone)] 207 | pub struct tls_crypto_info { 208 | pub version: __u16, 209 | pub cipher_type: __u16, 210 | } 211 | #[test] 212 | fn bindgen_test_layout_tls_crypto_info() { 213 | assert_eq!( 214 | ::std::mem::size_of::(), 215 | 4usize, 216 | concat!("Size of: ", stringify!(tls_crypto_info)) 217 | ); 218 | assert_eq!( 219 | ::std::mem::align_of::(), 220 | 2usize, 221 | concat!("Alignment of ", stringify!(tls_crypto_info)) 222 | ); 223 | fn test_field_version() { 224 | assert_eq!( 225 | unsafe { 226 | let uninit = ::std::mem::MaybeUninit::::uninit(); 227 | let ptr = uninit.as_ptr(); 228 | ::std::ptr::addr_of!((*ptr).version) as usize - ptr as usize 229 | }, 230 | 0usize, 231 | concat!( 232 | "Offset of field: ", 233 | stringify!(tls_crypto_info), 234 | "::", 235 | stringify!(version) 236 | ) 237 | ); 238 | } 239 | test_field_version(); 240 | fn test_field_cipher_type() { 241 | assert_eq!( 242 | unsafe { 243 | let uninit = ::std::mem::MaybeUninit::::uninit(); 244 | let ptr = uninit.as_ptr(); 245 | ::std::ptr::addr_of!((*ptr).cipher_type) as usize - ptr as usize 246 | }, 247 | 2usize, 248 | concat!( 249 | "Offset of field: ", 250 | stringify!(tls_crypto_info), 251 | "::", 252 | stringify!(cipher_type) 253 | ) 254 | ); 255 | } 256 | test_field_cipher_type(); 257 | } 258 | #[repr(C)] 259 | #[derive(Debug, Copy, Clone)] 260 | pub struct tls12_crypto_info_aes_gcm_128 { 261 | pub info: tls_crypto_info, 262 | pub iv: [::std::os::raw::c_uchar; 8usize], 263 | pub key: [::std::os::raw::c_uchar; 16usize], 264 | pub salt: [::std::os::raw::c_uchar; 4usize], 265 | pub rec_seq: [::std::os::raw::c_uchar; 8usize], 266 | } 267 | #[test] 268 | fn bindgen_test_layout_tls12_crypto_info_aes_gcm_128() { 269 | assert_eq!( 270 | ::std::mem::size_of::(), 271 | 40usize, 272 | concat!("Size of: ", stringify!(tls12_crypto_info_aes_gcm_128)) 273 | ); 274 | assert_eq!( 275 | ::std::mem::align_of::(), 276 | 2usize, 277 | concat!("Alignment of ", stringify!(tls12_crypto_info_aes_gcm_128)) 278 | ); 279 | fn test_field_info() { 280 | assert_eq!( 281 | unsafe { 282 | let uninit = ::std::mem::MaybeUninit::::uninit(); 283 | let ptr = uninit.as_ptr(); 284 | ::std::ptr::addr_of!((*ptr).info) as usize - ptr as usize 285 | }, 286 | 0usize, 287 | concat!( 288 | "Offset of field: ", 289 | stringify!(tls12_crypto_info_aes_gcm_128), 290 | "::", 291 | stringify!(info) 292 | ) 293 | ); 294 | } 295 | test_field_info(); 296 | fn test_field_iv() { 297 | assert_eq!( 298 | unsafe { 299 | let uninit = ::std::mem::MaybeUninit::::uninit(); 300 | let ptr = uninit.as_ptr(); 301 | ::std::ptr::addr_of!((*ptr).iv) as usize - ptr as usize 302 | }, 303 | 4usize, 304 | concat!( 305 | "Offset of field: ", 306 | stringify!(tls12_crypto_info_aes_gcm_128), 307 | "::", 308 | stringify!(iv) 309 | ) 310 | ); 311 | } 312 | test_field_iv(); 313 | fn test_field_key() { 314 | assert_eq!( 315 | unsafe { 316 | let uninit = ::std::mem::MaybeUninit::::uninit(); 317 | let ptr = uninit.as_ptr(); 318 | ::std::ptr::addr_of!((*ptr).key) as usize - ptr as usize 319 | }, 320 | 12usize, 321 | concat!( 322 | "Offset of field: ", 323 | stringify!(tls12_crypto_info_aes_gcm_128), 324 | "::", 325 | stringify!(key) 326 | ) 327 | ); 328 | } 329 | test_field_key(); 330 | fn test_field_salt() { 331 | assert_eq!( 332 | unsafe { 333 | let uninit = ::std::mem::MaybeUninit::::uninit(); 334 | let ptr = uninit.as_ptr(); 335 | ::std::ptr::addr_of!((*ptr).salt) as usize - ptr as usize 336 | }, 337 | 28usize, 338 | concat!( 339 | "Offset of field: ", 340 | stringify!(tls12_crypto_info_aes_gcm_128), 341 | "::", 342 | stringify!(salt) 343 | ) 344 | ); 345 | } 346 | test_field_salt(); 347 | fn test_field_rec_seq() { 348 | assert_eq!( 349 | unsafe { 350 | let uninit = ::std::mem::MaybeUninit::::uninit(); 351 | let ptr = uninit.as_ptr(); 352 | ::std::ptr::addr_of!((*ptr).rec_seq) as usize - ptr as usize 353 | }, 354 | 32usize, 355 | concat!( 356 | "Offset of field: ", 357 | stringify!(tls12_crypto_info_aes_gcm_128), 358 | "::", 359 | stringify!(rec_seq) 360 | ) 361 | ); 362 | } 363 | test_field_rec_seq(); 364 | } 365 | #[repr(C)] 366 | #[derive(Debug, Copy, Clone)] 367 | pub struct tls12_crypto_info_aes_gcm_256 { 368 | pub info: tls_crypto_info, 369 | pub iv: [::std::os::raw::c_uchar; 8usize], 370 | pub key: [::std::os::raw::c_uchar; 32usize], 371 | pub salt: [::std::os::raw::c_uchar; 4usize], 372 | pub rec_seq: [::std::os::raw::c_uchar; 8usize], 373 | } 374 | #[test] 375 | fn bindgen_test_layout_tls12_crypto_info_aes_gcm_256() { 376 | assert_eq!( 377 | ::std::mem::size_of::(), 378 | 56usize, 379 | concat!("Size of: ", stringify!(tls12_crypto_info_aes_gcm_256)) 380 | ); 381 | assert_eq!( 382 | ::std::mem::align_of::(), 383 | 2usize, 384 | concat!("Alignment of ", stringify!(tls12_crypto_info_aes_gcm_256)) 385 | ); 386 | fn test_field_info() { 387 | assert_eq!( 388 | unsafe { 389 | let uninit = ::std::mem::MaybeUninit::::uninit(); 390 | let ptr = uninit.as_ptr(); 391 | ::std::ptr::addr_of!((*ptr).info) as usize - ptr as usize 392 | }, 393 | 0usize, 394 | concat!( 395 | "Offset of field: ", 396 | stringify!(tls12_crypto_info_aes_gcm_256), 397 | "::", 398 | stringify!(info) 399 | ) 400 | ); 401 | } 402 | test_field_info(); 403 | fn test_field_iv() { 404 | assert_eq!( 405 | unsafe { 406 | let uninit = ::std::mem::MaybeUninit::::uninit(); 407 | let ptr = uninit.as_ptr(); 408 | ::std::ptr::addr_of!((*ptr).iv) as usize - ptr as usize 409 | }, 410 | 4usize, 411 | concat!( 412 | "Offset of field: ", 413 | stringify!(tls12_crypto_info_aes_gcm_256), 414 | "::", 415 | stringify!(iv) 416 | ) 417 | ); 418 | } 419 | test_field_iv(); 420 | fn test_field_key() { 421 | assert_eq!( 422 | unsafe { 423 | let uninit = ::std::mem::MaybeUninit::::uninit(); 424 | let ptr = uninit.as_ptr(); 425 | ::std::ptr::addr_of!((*ptr).key) as usize - ptr as usize 426 | }, 427 | 12usize, 428 | concat!( 429 | "Offset of field: ", 430 | stringify!(tls12_crypto_info_aes_gcm_256), 431 | "::", 432 | stringify!(key) 433 | ) 434 | ); 435 | } 436 | test_field_key(); 437 | fn test_field_salt() { 438 | assert_eq!( 439 | unsafe { 440 | let uninit = ::std::mem::MaybeUninit::::uninit(); 441 | let ptr = uninit.as_ptr(); 442 | ::std::ptr::addr_of!((*ptr).salt) as usize - ptr as usize 443 | }, 444 | 44usize, 445 | concat!( 446 | "Offset of field: ", 447 | stringify!(tls12_crypto_info_aes_gcm_256), 448 | "::", 449 | stringify!(salt) 450 | ) 451 | ); 452 | } 453 | test_field_salt(); 454 | fn test_field_rec_seq() { 455 | assert_eq!( 456 | unsafe { 457 | let uninit = ::std::mem::MaybeUninit::::uninit(); 458 | let ptr = uninit.as_ptr(); 459 | ::std::ptr::addr_of!((*ptr).rec_seq) as usize - ptr as usize 460 | }, 461 | 48usize, 462 | concat!( 463 | "Offset of field: ", 464 | stringify!(tls12_crypto_info_aes_gcm_256), 465 | "::", 466 | stringify!(rec_seq) 467 | ) 468 | ); 469 | } 470 | test_field_rec_seq(); 471 | } 472 | #[repr(C)] 473 | #[derive(Debug, Copy, Clone)] 474 | pub struct tls12_crypto_info_aes_ccm_128 { 475 | pub info: tls_crypto_info, 476 | pub iv: [::std::os::raw::c_uchar; 8usize], 477 | pub key: [::std::os::raw::c_uchar; 16usize], 478 | pub salt: [::std::os::raw::c_uchar; 4usize], 479 | pub rec_seq: [::std::os::raw::c_uchar; 8usize], 480 | } 481 | #[test] 482 | fn bindgen_test_layout_tls12_crypto_info_aes_ccm_128() { 483 | assert_eq!( 484 | ::std::mem::size_of::(), 485 | 40usize, 486 | concat!("Size of: ", stringify!(tls12_crypto_info_aes_ccm_128)) 487 | ); 488 | assert_eq!( 489 | ::std::mem::align_of::(), 490 | 2usize, 491 | concat!("Alignment of ", stringify!(tls12_crypto_info_aes_ccm_128)) 492 | ); 493 | fn test_field_info() { 494 | assert_eq!( 495 | unsafe { 496 | let uninit = ::std::mem::MaybeUninit::::uninit(); 497 | let ptr = uninit.as_ptr(); 498 | ::std::ptr::addr_of!((*ptr).info) as usize - ptr as usize 499 | }, 500 | 0usize, 501 | concat!( 502 | "Offset of field: ", 503 | stringify!(tls12_crypto_info_aes_ccm_128), 504 | "::", 505 | stringify!(info) 506 | ) 507 | ); 508 | } 509 | test_field_info(); 510 | fn test_field_iv() { 511 | assert_eq!( 512 | unsafe { 513 | let uninit = ::std::mem::MaybeUninit::::uninit(); 514 | let ptr = uninit.as_ptr(); 515 | ::std::ptr::addr_of!((*ptr).iv) as usize - ptr as usize 516 | }, 517 | 4usize, 518 | concat!( 519 | "Offset of field: ", 520 | stringify!(tls12_crypto_info_aes_ccm_128), 521 | "::", 522 | stringify!(iv) 523 | ) 524 | ); 525 | } 526 | test_field_iv(); 527 | fn test_field_key() { 528 | assert_eq!( 529 | unsafe { 530 | let uninit = ::std::mem::MaybeUninit::::uninit(); 531 | let ptr = uninit.as_ptr(); 532 | ::std::ptr::addr_of!((*ptr).key) as usize - ptr as usize 533 | }, 534 | 12usize, 535 | concat!( 536 | "Offset of field: ", 537 | stringify!(tls12_crypto_info_aes_ccm_128), 538 | "::", 539 | stringify!(key) 540 | ) 541 | ); 542 | } 543 | test_field_key(); 544 | fn test_field_salt() { 545 | assert_eq!( 546 | unsafe { 547 | let uninit = ::std::mem::MaybeUninit::::uninit(); 548 | let ptr = uninit.as_ptr(); 549 | ::std::ptr::addr_of!((*ptr).salt) as usize - ptr as usize 550 | }, 551 | 28usize, 552 | concat!( 553 | "Offset of field: ", 554 | stringify!(tls12_crypto_info_aes_ccm_128), 555 | "::", 556 | stringify!(salt) 557 | ) 558 | ); 559 | } 560 | test_field_salt(); 561 | fn test_field_rec_seq() { 562 | assert_eq!( 563 | unsafe { 564 | let uninit = ::std::mem::MaybeUninit::::uninit(); 565 | let ptr = uninit.as_ptr(); 566 | ::std::ptr::addr_of!((*ptr).rec_seq) as usize - ptr as usize 567 | }, 568 | 32usize, 569 | concat!( 570 | "Offset of field: ", 571 | stringify!(tls12_crypto_info_aes_ccm_128), 572 | "::", 573 | stringify!(rec_seq) 574 | ) 575 | ); 576 | } 577 | test_field_rec_seq(); 578 | } 579 | #[repr(C)] 580 | #[derive(Debug)] 581 | pub struct tls12_crypto_info_chacha20_poly1305 { 582 | pub info: tls_crypto_info, 583 | pub iv: [::std::os::raw::c_uchar; 12usize], 584 | pub key: [::std::os::raw::c_uchar; 32usize], 585 | pub salt: __IncompleteArrayField<::std::os::raw::c_uchar>, 586 | pub rec_seq: [::std::os::raw::c_uchar; 8usize], 587 | } 588 | #[test] 589 | fn bindgen_test_layout_tls12_crypto_info_chacha20_poly1305() { 590 | assert_eq!( 591 | ::std::mem::size_of::(), 592 | 56usize, 593 | concat!("Size of: ", stringify!(tls12_crypto_info_chacha20_poly1305)) 594 | ); 595 | assert_eq!( 596 | ::std::mem::align_of::(), 597 | 2usize, 598 | concat!( 599 | "Alignment of ", 600 | stringify!(tls12_crypto_info_chacha20_poly1305) 601 | ) 602 | ); 603 | fn test_field_info() { 604 | assert_eq!( 605 | unsafe { 606 | let uninit = 607 | ::std::mem::MaybeUninit::::uninit(); 608 | let ptr = uninit.as_ptr(); 609 | ::std::ptr::addr_of!((*ptr).info) as usize - ptr as usize 610 | }, 611 | 0usize, 612 | concat!( 613 | "Offset of field: ", 614 | stringify!(tls12_crypto_info_chacha20_poly1305), 615 | "::", 616 | stringify!(info) 617 | ) 618 | ); 619 | } 620 | test_field_info(); 621 | fn test_field_iv() { 622 | assert_eq!( 623 | unsafe { 624 | let uninit = 625 | ::std::mem::MaybeUninit::::uninit(); 626 | let ptr = uninit.as_ptr(); 627 | ::std::ptr::addr_of!((*ptr).iv) as usize - ptr as usize 628 | }, 629 | 4usize, 630 | concat!( 631 | "Offset of field: ", 632 | stringify!(tls12_crypto_info_chacha20_poly1305), 633 | "::", 634 | stringify!(iv) 635 | ) 636 | ); 637 | } 638 | test_field_iv(); 639 | fn test_field_key() { 640 | assert_eq!( 641 | unsafe { 642 | let uninit = 643 | ::std::mem::MaybeUninit::::uninit(); 644 | let ptr = uninit.as_ptr(); 645 | ::std::ptr::addr_of!((*ptr).key) as usize - ptr as usize 646 | }, 647 | 16usize, 648 | concat!( 649 | "Offset of field: ", 650 | stringify!(tls12_crypto_info_chacha20_poly1305), 651 | "::", 652 | stringify!(key) 653 | ) 654 | ); 655 | } 656 | test_field_key(); 657 | fn test_field_salt() { 658 | assert_eq!( 659 | unsafe { 660 | let uninit = 661 | ::std::mem::MaybeUninit::::uninit(); 662 | let ptr = uninit.as_ptr(); 663 | ::std::ptr::addr_of!((*ptr).salt) as usize - ptr as usize 664 | }, 665 | 48usize, 666 | concat!( 667 | "Offset of field: ", 668 | stringify!(tls12_crypto_info_chacha20_poly1305), 669 | "::", 670 | stringify!(salt) 671 | ) 672 | ); 673 | } 674 | test_field_salt(); 675 | fn test_field_rec_seq() { 676 | assert_eq!( 677 | unsafe { 678 | let uninit = 679 | ::std::mem::MaybeUninit::::uninit(); 680 | let ptr = uninit.as_ptr(); 681 | ::std::ptr::addr_of!((*ptr).rec_seq) as usize - ptr as usize 682 | }, 683 | 48usize, 684 | concat!( 685 | "Offset of field: ", 686 | stringify!(tls12_crypto_info_chacha20_poly1305), 687 | "::", 688 | stringify!(rec_seq) 689 | ) 690 | ); 691 | } 692 | test_field_rec_seq(); 693 | } 694 | #[repr(C)] 695 | #[derive(Debug, Copy, Clone)] 696 | pub struct tls12_crypto_info_sm4_gcm { 697 | pub info: tls_crypto_info, 698 | pub iv: [::std::os::raw::c_uchar; 8usize], 699 | pub key: [::std::os::raw::c_uchar; 16usize], 700 | pub salt: [::std::os::raw::c_uchar; 4usize], 701 | pub rec_seq: [::std::os::raw::c_uchar; 8usize], 702 | } 703 | #[test] 704 | fn bindgen_test_layout_tls12_crypto_info_sm4_gcm() { 705 | assert_eq!( 706 | ::std::mem::size_of::(), 707 | 40usize, 708 | concat!("Size of: ", stringify!(tls12_crypto_info_sm4_gcm)) 709 | ); 710 | assert_eq!( 711 | ::std::mem::align_of::(), 712 | 2usize, 713 | concat!("Alignment of ", stringify!(tls12_crypto_info_sm4_gcm)) 714 | ); 715 | fn test_field_info() { 716 | assert_eq!( 717 | unsafe { 718 | let uninit = ::std::mem::MaybeUninit::::uninit(); 719 | let ptr = uninit.as_ptr(); 720 | ::std::ptr::addr_of!((*ptr).info) as usize - ptr as usize 721 | }, 722 | 0usize, 723 | concat!( 724 | "Offset of field: ", 725 | stringify!(tls12_crypto_info_sm4_gcm), 726 | "::", 727 | stringify!(info) 728 | ) 729 | ); 730 | } 731 | test_field_info(); 732 | fn test_field_iv() { 733 | assert_eq!( 734 | unsafe { 735 | let uninit = ::std::mem::MaybeUninit::::uninit(); 736 | let ptr = uninit.as_ptr(); 737 | ::std::ptr::addr_of!((*ptr).iv) as usize - ptr as usize 738 | }, 739 | 4usize, 740 | concat!( 741 | "Offset of field: ", 742 | stringify!(tls12_crypto_info_sm4_gcm), 743 | "::", 744 | stringify!(iv) 745 | ) 746 | ); 747 | } 748 | test_field_iv(); 749 | fn test_field_key() { 750 | assert_eq!( 751 | unsafe { 752 | let uninit = ::std::mem::MaybeUninit::::uninit(); 753 | let ptr = uninit.as_ptr(); 754 | ::std::ptr::addr_of!((*ptr).key) as usize - ptr as usize 755 | }, 756 | 12usize, 757 | concat!( 758 | "Offset of field: ", 759 | stringify!(tls12_crypto_info_sm4_gcm), 760 | "::", 761 | stringify!(key) 762 | ) 763 | ); 764 | } 765 | test_field_key(); 766 | fn test_field_salt() { 767 | assert_eq!( 768 | unsafe { 769 | let uninit = ::std::mem::MaybeUninit::::uninit(); 770 | let ptr = uninit.as_ptr(); 771 | ::std::ptr::addr_of!((*ptr).salt) as usize - ptr as usize 772 | }, 773 | 28usize, 774 | concat!( 775 | "Offset of field: ", 776 | stringify!(tls12_crypto_info_sm4_gcm), 777 | "::", 778 | stringify!(salt) 779 | ) 780 | ); 781 | } 782 | test_field_salt(); 783 | fn test_field_rec_seq() { 784 | assert_eq!( 785 | unsafe { 786 | let uninit = ::std::mem::MaybeUninit::::uninit(); 787 | let ptr = uninit.as_ptr(); 788 | ::std::ptr::addr_of!((*ptr).rec_seq) as usize - ptr as usize 789 | }, 790 | 32usize, 791 | concat!( 792 | "Offset of field: ", 793 | stringify!(tls12_crypto_info_sm4_gcm), 794 | "::", 795 | stringify!(rec_seq) 796 | ) 797 | ); 798 | } 799 | test_field_rec_seq(); 800 | } 801 | #[repr(C)] 802 | #[derive(Debug, Copy, Clone)] 803 | pub struct tls12_crypto_info_sm4_ccm { 804 | pub info: tls_crypto_info, 805 | pub iv: [::std::os::raw::c_uchar; 8usize], 806 | pub key: [::std::os::raw::c_uchar; 16usize], 807 | pub salt: [::std::os::raw::c_uchar; 4usize], 808 | pub rec_seq: [::std::os::raw::c_uchar; 8usize], 809 | } 810 | #[test] 811 | fn bindgen_test_layout_tls12_crypto_info_sm4_ccm() { 812 | assert_eq!( 813 | ::std::mem::size_of::(), 814 | 40usize, 815 | concat!("Size of: ", stringify!(tls12_crypto_info_sm4_ccm)) 816 | ); 817 | assert_eq!( 818 | ::std::mem::align_of::(), 819 | 2usize, 820 | concat!("Alignment of ", stringify!(tls12_crypto_info_sm4_ccm)) 821 | ); 822 | fn test_field_info() { 823 | assert_eq!( 824 | unsafe { 825 | let uninit = ::std::mem::MaybeUninit::::uninit(); 826 | let ptr = uninit.as_ptr(); 827 | ::std::ptr::addr_of!((*ptr).info) as usize - ptr as usize 828 | }, 829 | 0usize, 830 | concat!( 831 | "Offset of field: ", 832 | stringify!(tls12_crypto_info_sm4_ccm), 833 | "::", 834 | stringify!(info) 835 | ) 836 | ); 837 | } 838 | test_field_info(); 839 | fn test_field_iv() { 840 | assert_eq!( 841 | unsafe { 842 | let uninit = ::std::mem::MaybeUninit::::uninit(); 843 | let ptr = uninit.as_ptr(); 844 | ::std::ptr::addr_of!((*ptr).iv) as usize - ptr as usize 845 | }, 846 | 4usize, 847 | concat!( 848 | "Offset of field: ", 849 | stringify!(tls12_crypto_info_sm4_ccm), 850 | "::", 851 | stringify!(iv) 852 | ) 853 | ); 854 | } 855 | test_field_iv(); 856 | fn test_field_key() { 857 | assert_eq!( 858 | unsafe { 859 | let uninit = ::std::mem::MaybeUninit::::uninit(); 860 | let ptr = uninit.as_ptr(); 861 | ::std::ptr::addr_of!((*ptr).key) as usize - ptr as usize 862 | }, 863 | 12usize, 864 | concat!( 865 | "Offset of field: ", 866 | stringify!(tls12_crypto_info_sm4_ccm), 867 | "::", 868 | stringify!(key) 869 | ) 870 | ); 871 | } 872 | test_field_key(); 873 | fn test_field_salt() { 874 | assert_eq!( 875 | unsafe { 876 | let uninit = ::std::mem::MaybeUninit::::uninit(); 877 | let ptr = uninit.as_ptr(); 878 | ::std::ptr::addr_of!((*ptr).salt) as usize - ptr as usize 879 | }, 880 | 28usize, 881 | concat!( 882 | "Offset of field: ", 883 | stringify!(tls12_crypto_info_sm4_ccm), 884 | "::", 885 | stringify!(salt) 886 | ) 887 | ); 888 | } 889 | test_field_salt(); 890 | fn test_field_rec_seq() { 891 | assert_eq!( 892 | unsafe { 893 | let uninit = ::std::mem::MaybeUninit::::uninit(); 894 | let ptr = uninit.as_ptr(); 895 | ::std::ptr::addr_of!((*ptr).rec_seq) as usize - ptr as usize 896 | }, 897 | 32usize, 898 | concat!( 899 | "Offset of field: ", 900 | stringify!(tls12_crypto_info_sm4_ccm), 901 | "::", 902 | stringify!(rec_seq) 903 | ) 904 | ); 905 | } 906 | test_field_rec_seq(); 907 | } 908 | pub const TLS_INFO_UNSPEC: _bindgen_ty_1 = 0; 909 | pub const TLS_INFO_VERSION: _bindgen_ty_1 = 1; 910 | pub const TLS_INFO_CIPHER: _bindgen_ty_1 = 2; 911 | pub const TLS_INFO_TXCONF: _bindgen_ty_1 = 3; 912 | pub const TLS_INFO_RXCONF: _bindgen_ty_1 = 4; 913 | pub const TLS_INFO_ZC_RO_TX: _bindgen_ty_1 = 5; 914 | pub const TLS_INFO_RX_NO_PAD: _bindgen_ty_1 = 6; 915 | pub const __TLS_INFO_MAX: _bindgen_ty_1 = 7; 916 | pub type _bindgen_ty_1 = ::std::os::raw::c_uint; 917 | -------------------------------------------------------------------------------- /ktls-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(non_snake_case)] 3 | 4 | pub mod bindings; 5 | -------------------------------------------------------------------------------- /ktls-sys/tls.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR Linux-OpenIB) */ 2 | /* 3 | * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved. 4 | * 5 | * This software is available to you under a choice of one of two 6 | * licenses. You may choose to be licensed under the terms of the GNU 7 | * General Public License (GPL) Version 2, available from the file 8 | * COPYING in the main directory of this source tree, or the 9 | * OpenIB.org BSD license below: 10 | * 11 | * Redistribution and use in source and binary forms, with or 12 | * without modification, are permitted provided that the following 13 | * conditions are met: 14 | * 15 | * - Redistributions of source code must retain the above 16 | * copyright notice, this list of conditions and the following 17 | * disclaimer. 18 | * 19 | * - Redistributions in binary form must reproduce the above 20 | * copyright notice, this list of conditions and the following 21 | * disclaimer in the documentation and/or other materials 22 | * provided with the distribution. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | * SOFTWARE. 32 | */ 33 | 34 | #ifndef _UAPI_LINUX_TLS_H 35 | #define _UAPI_LINUX_TLS_H 36 | 37 | #include 38 | 39 | /* TLS socket options */ 40 | #define TLS_TX 1 /* Set transmit parameters */ 41 | #define TLS_RX 2 /* Set receive parameters */ 42 | #define TLS_TX_ZEROCOPY_RO 3 /* TX zerocopy (only sendfile now) */ 43 | #define TLS_RX_EXPECT_NO_PAD 4 /* Attempt opportunistic zero-copy */ 44 | 45 | /* Supported versions */ 46 | #define TLS_VERSION_MINOR(ver) ((ver) & 0xFF) 47 | #define TLS_VERSION_MAJOR(ver) (((ver) >> 8) & 0xFF) 48 | 49 | #define TLS_VERSION_NUMBER(id) ((((id##_VERSION_MAJOR) & 0xFF) << 8) | \ 50 | ((id##_VERSION_MINOR) & 0xFF)) 51 | 52 | #define TLS_1_2_VERSION_MAJOR 0x3 53 | #define TLS_1_2_VERSION_MINOR 0x3 54 | #define TLS_1_2_VERSION TLS_VERSION_NUMBER(TLS_1_2) 55 | 56 | #define TLS_1_3_VERSION_MAJOR 0x3 57 | #define TLS_1_3_VERSION_MINOR 0x4 58 | #define TLS_1_3_VERSION TLS_VERSION_NUMBER(TLS_1_3) 59 | 60 | /* Supported ciphers */ 61 | #define TLS_CIPHER_AES_GCM_128 51 62 | #define TLS_CIPHER_AES_GCM_128_IV_SIZE 8 63 | #define TLS_CIPHER_AES_GCM_128_KEY_SIZE 16 64 | #define TLS_CIPHER_AES_GCM_128_SALT_SIZE 4 65 | #define TLS_CIPHER_AES_GCM_128_TAG_SIZE 16 66 | #define TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE 8 67 | 68 | #define TLS_CIPHER_AES_GCM_256 52 69 | #define TLS_CIPHER_AES_GCM_256_IV_SIZE 8 70 | #define TLS_CIPHER_AES_GCM_256_KEY_SIZE 32 71 | #define TLS_CIPHER_AES_GCM_256_SALT_SIZE 4 72 | #define TLS_CIPHER_AES_GCM_256_TAG_SIZE 16 73 | #define TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE 8 74 | 75 | #define TLS_CIPHER_AES_CCM_128 53 76 | #define TLS_CIPHER_AES_CCM_128_IV_SIZE 8 77 | #define TLS_CIPHER_AES_CCM_128_KEY_SIZE 16 78 | #define TLS_CIPHER_AES_CCM_128_SALT_SIZE 4 79 | #define TLS_CIPHER_AES_CCM_128_TAG_SIZE 16 80 | #define TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE 8 81 | 82 | #define TLS_CIPHER_CHACHA20_POLY1305 54 83 | #define TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE 12 84 | #define TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE 32 85 | #define TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE 0 86 | #define TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE 16 87 | #define TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE 8 88 | 89 | #define TLS_CIPHER_SM4_GCM 55 90 | #define TLS_CIPHER_SM4_GCM_IV_SIZE 8 91 | #define TLS_CIPHER_SM4_GCM_KEY_SIZE 16 92 | #define TLS_CIPHER_SM4_GCM_SALT_SIZE 4 93 | #define TLS_CIPHER_SM4_GCM_TAG_SIZE 16 94 | #define TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE 8 95 | 96 | #define TLS_CIPHER_SM4_CCM 56 97 | #define TLS_CIPHER_SM4_CCM_IV_SIZE 8 98 | #define TLS_CIPHER_SM4_CCM_KEY_SIZE 16 99 | #define TLS_CIPHER_SM4_CCM_SALT_SIZE 4 100 | #define TLS_CIPHER_SM4_CCM_TAG_SIZE 16 101 | #define TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE 8 102 | 103 | #define TLS_SET_RECORD_TYPE 1 104 | #define TLS_GET_RECORD_TYPE 2 105 | 106 | struct tls_crypto_info { 107 | __u16 version; 108 | __u16 cipher_type; 109 | }; 110 | 111 | struct tls12_crypto_info_aes_gcm_128 { 112 | struct tls_crypto_info info; 113 | unsigned char iv[TLS_CIPHER_AES_GCM_128_IV_SIZE]; 114 | unsigned char key[TLS_CIPHER_AES_GCM_128_KEY_SIZE]; 115 | unsigned char salt[TLS_CIPHER_AES_GCM_128_SALT_SIZE]; 116 | unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE]; 117 | }; 118 | 119 | struct tls12_crypto_info_aes_gcm_256 { 120 | struct tls_crypto_info info; 121 | unsigned char iv[TLS_CIPHER_AES_GCM_256_IV_SIZE]; 122 | unsigned char key[TLS_CIPHER_AES_GCM_256_KEY_SIZE]; 123 | unsigned char salt[TLS_CIPHER_AES_GCM_256_SALT_SIZE]; 124 | unsigned char rec_seq[TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE]; 125 | }; 126 | 127 | struct tls12_crypto_info_aes_ccm_128 { 128 | struct tls_crypto_info info; 129 | unsigned char iv[TLS_CIPHER_AES_CCM_128_IV_SIZE]; 130 | unsigned char key[TLS_CIPHER_AES_CCM_128_KEY_SIZE]; 131 | unsigned char salt[TLS_CIPHER_AES_CCM_128_SALT_SIZE]; 132 | unsigned char rec_seq[TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE]; 133 | }; 134 | 135 | struct tls12_crypto_info_chacha20_poly1305 { 136 | struct tls_crypto_info info; 137 | unsigned char iv[TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE]; 138 | unsigned char key[TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE]; 139 | unsigned char salt[TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE]; 140 | unsigned char rec_seq[TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE]; 141 | }; 142 | 143 | struct tls12_crypto_info_sm4_gcm { 144 | struct tls_crypto_info info; 145 | unsigned char iv[TLS_CIPHER_SM4_GCM_IV_SIZE]; 146 | unsigned char key[TLS_CIPHER_SM4_GCM_KEY_SIZE]; 147 | unsigned char salt[TLS_CIPHER_SM4_GCM_SALT_SIZE]; 148 | unsigned char rec_seq[TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE]; 149 | }; 150 | 151 | struct tls12_crypto_info_sm4_ccm { 152 | struct tls_crypto_info info; 153 | unsigned char iv[TLS_CIPHER_SM4_CCM_IV_SIZE]; 154 | unsigned char key[TLS_CIPHER_SM4_CCM_KEY_SIZE]; 155 | unsigned char salt[TLS_CIPHER_SM4_CCM_SALT_SIZE]; 156 | unsigned char rec_seq[TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE]; 157 | }; 158 | 159 | enum { 160 | TLS_INFO_UNSPEC, 161 | TLS_INFO_VERSION, 162 | TLS_INFO_CIPHER, 163 | TLS_INFO_TXCONF, 164 | TLS_INFO_RXCONF, 165 | TLS_INFO_ZC_RO_TX, 166 | TLS_INFO_RX_NO_PAD, 167 | __TLS_INFO_MAX, 168 | }; 169 | #define TLS_INFO_MAX (__TLS_INFO_MAX - 1) 170 | 171 | #define TLS_CONF_BASE 1 172 | #define TLS_CONF_SW 2 173 | #define TLS_CONF_HW 3 174 | #define TLS_CONF_HW_RECORD 4 175 | 176 | #endif /* _UAPI_LINUX_TLS_H */ -------------------------------------------------------------------------------- /ktls/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [6.0.2](https://github.com/rustls/ktls/compare/ktls-v6.0.1...ktls-v6.0.2) - 2025-04-07 4 | 5 | ### Other 6 | 7 | - Make CryptoInfo public to enable asynchronous kTLS setup via io-uring or any mechanism other 8 | than ktls's default synchronous `setsockopt` call, cf. 9 | 10 | ## [6.0.1](https://github.com/rustls/ktls/compare/ktls-v6.0.0...ktls-v6.0.1) - 2024-09-26 11 | 12 | ### Other 13 | 14 | - Standardize READMEs a little 15 | - Turn ktls into a workspace 16 | 17 | ## [6.0.0](https://github.com/bearcove/ktls/compare/v5.0.0...v6.0.0) - 2024-08-14 18 | 19 | ### Added 20 | - [**breaking**] Upgrade other dependencies 21 | - [**breaking**] Upgrade to tokio-rustls 0.26.0, remove unused trait 22 | - Migrate from ktls-recvmsg to nix 0.28 23 | - Switch from futures to futures_util 24 | - Add aws-lc-rs as an alias for aws_lc_rs feature 25 | - Upgrade dependencies 26 | 27 | ### Fixed 28 | - Upgrade to Rust 1.80.1 29 | - Remove nix stuff 30 | 31 | ### Other 32 | - Bump rustls from 0.23.2 to 0.23.5 33 | - Remove Cargo.lock from .gitignore 34 | - Upgrade ktls-sys 35 | - Ignore .DS_Store files 36 | - Bump tokio-rustls 37 | - make aws_lc_rs and ring mutually exclusive for ktls 38 | - More aws_lc_rs support 39 | - Start adding support for aws_lc_rs 40 | 41 | ## [5.0.0](https://github.com/hapsoc/ktls/compare/v4.0.0...v5.0.0) - 2024-03-11 42 | 43 | ### Added 44 | - [**breaking**] Upgrade to rustls 0.22.2 45 | - [**breaking**] Upgrade to tokio-rustls 0.25.0 46 | 47 | ### Other 48 | - Get rid of constrandom (it caused 'wrong type' compile error on Rust stable) 49 | - Print backtraces in CI 50 | - Add nix flake to have the toolchain everywhere 51 | - Re-add Cargo.lock as per updated best practices 52 | - Disable incremental compilation 53 | - Remove token 54 | - Install missing tools 55 | - Use the sccache action 56 | - Just straight up try running it on GitHub-hosted runners 57 | 58 | ## [4.0.0](https://github.com/hapsoc/ktls/compare/v3.0.2...v4.0.0) - 2023-10-08 59 | 60 | ### Fixed 61 | - [**breaking**] Remove drained_remaining public method 62 | 63 | ### Other 64 | - Add more test coverage 65 | - Remove more explicit libc::close calls 66 | - Clarifies what this.inner.poll_shutdown does 67 | - Improve integration tests: try reading/writing after close, catch errors from both sides 68 | - Don't forget to close fd on writer side 69 | - Simplify/clarify code around alerts 70 | - Use enums to 'parse' TLS alerts 71 | - Depend on ktls-recvmsg v0.1.3 72 | - Remove panic, ktls may send unfinished alert msg 73 | - assert instead of asser_eq 74 | - Adding edge case in integration test for session shutdown 75 | - Properly handle critical alerts 76 | - Add crates.io badge 77 | - Use Rust stable for tests 78 | 79 | ## [3.0.2](https://github.com/hapsoc/ktls/compare/v3.0.1...v3.0.2) - 2023-10-02 80 | 81 | ### Other 82 | - Create FUNDING.yml 83 | - Upgrade rcgen to 0.11.3 84 | - Upgrade dependencies 85 | 86 | ## 3.0.1 (unreleased) 87 | 88 | Fix test suite (follow rustls' `ClientConfig::enable_tickets` transition to 89 | `ClientConfig::resumption`). 90 | 91 | ## 3.0.0 (2023-06-14) (yanked) 92 | 93 | Upgrade to tokio-rustls 0.24.1 94 | 95 | ## 2.0.0 (2023-03-29) 96 | 97 | Comes with a bunch of breaking changes, necessary to address some issues. 98 | 99 | Essentially, the rustls stream wasn't being drained properly in 100 | `config_ktls_{client,server}`. Doing this properly required introducing 101 | `CorkStream`, which is TLS-framing-aware. 102 | 103 | As a result, `config_ktls_*` functions now take a `TlsStream>` 104 | (where `IO` is typically `TcpStream`), and are async, since to properly drain we 105 | might need to read till the end of the last TLS messages rustls has partially 106 | buffered. 107 | 108 | ## 1.0.1 (2022-10-21) 109 | 110 | Initial release. 111 | -------------------------------------------------------------------------------- /ktls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ktls" 3 | version = "6.0.2" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | repository = "https://github.com/rustls/ktls" 7 | documentation = "https://docs.rs/ktls" 8 | authors = ["Amos Wenger "] 9 | readme = "README.md" 10 | description = """ 11 | Configures kTLS for tokio-rustls client and server connections. 12 | """ 13 | rust-version = "1.75" 14 | 15 | [dependencies] 16 | libc = { version = "0.2.155", features = ["const-extern-fn"] } 17 | thiserror = "2" 18 | tracing = "0.1.40" 19 | tokio-rustls = { default-features = false, version = "0.26.0" } 20 | rustls = { version = "0.23.12", default-features = false } 21 | smallvec = "1.13.2" 22 | memoffset = "0.9.1" 23 | pin-project-lite = "0.2.14" 24 | tokio = { version = "1.39.2", features = ["net", "macros", "io-util"] } 25 | ktls-sys = "1.0.1" 26 | num_enum = "0.7.3" 27 | futures-util = "0.3.30" 28 | nix = { version = "0.29.0", features = ["socket", "uio", "net"] } 29 | 30 | [dev-dependencies] 31 | lazy_static = "1.5.0" 32 | oorandom = "11.1.4" 33 | rcgen = "0.13.1" 34 | socket2 = "0.5.7" 35 | test-case = "3.3.1" 36 | tokio = { version = "1.39.2", features = ["full"] } 37 | tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } 38 | 39 | [features] 40 | default = ["aws_lc_rs", "tls12"] 41 | aws_lc_rs = ["rustls/aws_lc_rs", "tokio-rustls/aws_lc_rs"] 42 | aws-lc-rs = ["aws_lc_rs"] # Alias because Cargo features commonly use `-` 43 | ring = ["rustls/ring", "tokio-rustls/ring"] 44 | tls12 = ["rustls/tls12", "tokio-rustls/tls12"] 45 | -------------------------------------------------------------------------------- /ktls/README.md: -------------------------------------------------------------------------------- 1 | [![test pipeline](https://github.com/hapsoc/ktls/actions/workflows/test.yml/badge.svg)](https://github.com/hapsoc/ktls/actions/workflows/test.yml?query=branch%3Amain) 2 | [![Coverage Status (codecov.io)](https://codecov.io/gh/hapsoc/ktls/branch/main/graph/badge.svg)](https://codecov.io/gh/hapsoc/ktls/) 3 | [![Crates.io](https://img.shields.io/crates/v/ktls)](https://crates.io/crates/ktls) 4 | [![license: MIT/Apache-2.0](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE-MIT) 5 | 6 | # ktls 7 | 8 | Configures kTLS ([kernel TLS 9 | offload](https://www.kernel.org/doc/html/latest/networking/tls-offload.html)) 10 | for any type that implements `AsRawFd`, given a rustls `ServerConnection`. 11 | 12 | ## License 13 | 14 | This project is primarily distributed under the terms of both the MIT license 15 | and the Apache License (Version 2.0). 16 | 17 | See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details. 18 | -------------------------------------------------------------------------------- /ktls/src/async_read_ready.rs: -------------------------------------------------------------------------------- 1 | use std::{io, task}; 2 | 3 | pub trait AsyncReadReady { 4 | /// cf. https://docs.rs/tokio/latest/tokio/net/struct.TcpStream.html#method.poll_read_ready 5 | fn poll_read_ready(&self, cx: &mut task::Context<'_>) -> task::Poll>; 6 | } 7 | 8 | impl AsyncReadReady for tokio::net::TcpStream { 9 | fn poll_read_ready(&self, cx: &mut task::Context<'_>) -> task::Poll> { 10 | tokio::net::TcpStream::poll_read_ready(self, cx) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ktls/src/cork_stream.rs: -------------------------------------------------------------------------------- 1 | use std::{io, pin::Pin, task}; 2 | 3 | use rustls::internal::msgs::codec::Codec; 4 | use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; 5 | 6 | use crate::AsyncReadReady; 7 | 8 | enum State { 9 | ReadHeader { header_buf: [u8; 5], offset: usize }, 10 | ReadPayload { msg_size: usize, offset: usize }, 11 | // we encountered EOF while reading, or saw an invalid header and we're just 12 | // passing reads through without doing any sort of processing now. 13 | Passthrough, 14 | } 15 | 16 | /// This is a wrapper that reads TLS message headers so it knows when to start 17 | /// doing empty reads at the message boundary when "draining" a rustls 18 | /// connection before setting up kTLS for it. 19 | /// 20 | /// The short explanation is: rustls might have buffered one or more 21 | /// ApplicationData messages (the last one might even be partial) by the time 22 | /// "connect" / "accept" returns. 23 | /// 24 | /// We not only need to pop messages rustls has already deframed (that's done in 25 | /// a drain function elsewhere), but also let rustls finish reading and 26 | /// deframing any partial message it may have already buffered. 27 | /// 28 | /// Because this wrapper is trying very hard not to do any error handling, if it 29 | /// encounters anything that doesn't look like a TLS header (unknown type, 30 | /// nonsensical size, unexpected EOF), it'll quite easily fall back to a 31 | /// "passthrough" mode with no internal buffering, letting rustls take care 32 | /// reporting any errors. 33 | pub struct CorkStream { 34 | pub io: IO, 35 | // if true, causes empty reads at the message boudnary 36 | pub corked: bool, 37 | state: State, 38 | } 39 | 40 | impl CorkStream { 41 | pub fn new(io: IO) -> Self { 42 | Self { 43 | io, 44 | corked: false, 45 | state: State::ReadHeader { 46 | header_buf: Default::default(), 47 | offset: 0, 48 | }, 49 | } 50 | } 51 | } 52 | 53 | impl AsyncRead for CorkStream 54 | where 55 | IO: AsyncRead, 56 | { 57 | #[inline] 58 | fn poll_read( 59 | self: Pin<&mut Self>, 60 | cx: &mut task::Context<'_>, 61 | buf: &mut ReadBuf<'_>, 62 | ) -> task::Poll> { 63 | let this = unsafe { self.get_unchecked_mut() }; 64 | let mut io = unsafe { Pin::new_unchecked(&mut this.io) }; 65 | 66 | let state = &mut this.state; 67 | 68 | loop { 69 | match state { 70 | State::ReadHeader { header_buf, offset } => { 71 | if *offset == 0 && this.corked { 72 | tracing::trace!( 73 | "corked, returning empty read (but waking to prevent stalls)" 74 | ); 75 | cx.waker().wake_by_ref(); 76 | return task::Poll::Ready(Ok(())); 77 | } 78 | 79 | let left = header_buf.len() - *offset; 80 | tracing::trace!("reading header, {left}/{} bytes left", header_buf.len()); 81 | 82 | { 83 | let mut rest = ReadBuf::new(&mut header_buf[*offset..]); 84 | tracing::trace!("reading header: doing i/o"); 85 | futures_util::ready!(io.as_mut().poll_read(cx, &mut rest)?); 86 | tracing::trace!("reading header: io was ready"); 87 | *offset += rest.filled().len(); 88 | if rest.filled().is_empty() { 89 | // that's an unexpected EOF for sure, but let's have 90 | // rustls deal with the error reporting shall we? 91 | tracing::trace!( 92 | "unexpected EOF: header cut short after {} bytes", 93 | *offset 94 | ); 95 | buf.put_slice(&header_buf[..*offset]); 96 | *state = State::Passthrough; 97 | 98 | return task::Poll::Ready(Ok(())); 99 | } 100 | tracing::trace!("read {} bytes off of header", rest.filled().len()); 101 | } 102 | 103 | if *offset == 5 { 104 | // TODO: handle cases where buffer has less than 5 bytes 105 | // remaining. I (fasterthanlime) bet this never happens in 106 | // practice since the rustls deframer uses `copy_within` to 107 | // get rid of the part of the buffer it's already decoded. 108 | assert!(buf.remaining() >= 5, "you found an edge case in ktls!"); 109 | buf.put_slice(&header_buf[..]); 110 | 111 | match decode_header(*header_buf) { 112 | Some((typ, version, len)) => { 113 | tracing::trace!( 114 | "read header: typ={typ:?}, version={version:?}, len={len}" 115 | ); 116 | *state = State::ReadPayload { 117 | msg_size: len as usize, 118 | offset: 0, 119 | }; 120 | } 121 | None => { 122 | // we encountered an invalid header, let's bail out 123 | tracing::warn!("encountered invalid header, bailing out"); 124 | *state = State::Passthrough; 125 | } 126 | } 127 | 128 | return task::Poll::Ready(Ok(())); 129 | } else { 130 | // keep trying 131 | } 132 | } 133 | State::ReadPayload { msg_size, offset } => { 134 | let rest = *msg_size - *offset; 135 | 136 | let just_read = { 137 | let mut rest = buf.take(rest); 138 | futures_util::ready!(io.as_mut().poll_read(cx, &mut rest)?); 139 | 140 | tracing::trace!("read {} bytes off of payload", rest.filled().len()); 141 | *offset += rest.filled().len(); 142 | 143 | if *offset == *msg_size { 144 | tracing::trace!("read full payload (all {} bytes)", *offset); 145 | *state = State::ReadHeader { 146 | header_buf: Default::default(), 147 | offset: 0, 148 | }; 149 | } 150 | 151 | rest.filled().len() 152 | }; 153 | 154 | let new_filled = buf.filled().len() + just_read; 155 | buf.set_filled(new_filled); 156 | 157 | return task::Poll::Ready(Ok(())); 158 | } 159 | State::Passthrough => { 160 | // we encountered EOF while reading, or saw an invalid header and we're just 161 | // passing reads through without doing any sort of processing now. 162 | return io.poll_read(cx, buf); 163 | } 164 | } 165 | } 166 | } 167 | } 168 | 169 | impl AsyncReadReady for CorkStream 170 | where 171 | IO: AsyncReadReady, 172 | { 173 | fn poll_read_ready(&self, cx: &mut task::Context<'_>) -> task::Poll> { 174 | self.io.poll_read_ready(cx) 175 | } 176 | } 177 | 178 | impl AsyncWrite for CorkStream 179 | where 180 | IO: AsyncWrite, 181 | { 182 | #[inline] 183 | fn poll_write( 184 | self: Pin<&mut Self>, 185 | cx: &mut task::Context<'_>, 186 | buf: &[u8], 187 | ) -> task::Poll> { 188 | let io = unsafe { self.map_unchecked_mut(|s| &mut s.io) }; 189 | io.poll_write(cx, buf) 190 | } 191 | 192 | #[inline] 193 | fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll> { 194 | let io = unsafe { self.map_unchecked_mut(|s| &mut s.io) }; 195 | io.poll_flush(cx) 196 | } 197 | 198 | #[inline] 199 | fn poll_shutdown( 200 | self: Pin<&mut Self>, 201 | cx: &mut task::Context<'_>, 202 | ) -> task::Poll> { 203 | let io = unsafe { self.map_unchecked_mut(|s| &mut s.io) }; 204 | io.poll_shutdown(cx) 205 | } 206 | } 207 | 208 | fn decode_header(b: [u8; 5]) -> Option<(rustls::ContentType, rustls::ProtocolVersion, u16)> { 209 | let typ = rustls::ContentType::read_bytes(&b[0..1]).ok()?; 210 | let version = rustls::ProtocolVersion::read_bytes(&b[1..3]).ok()?; 211 | // this is dumb but it looks less scary than `.try_into().unwrap()`: 212 | let len: u16 = u16::from_be_bytes([b[3], b[4]]); 213 | Some((typ, version, len)) 214 | } 215 | -------------------------------------------------------------------------------- /ktls/src/ffi.rs: -------------------------------------------------------------------------------- 1 | use std::os::unix::prelude::RawFd; 2 | 3 | use ktls_sys::bindings as ktls; 4 | use rustls::{ 5 | internal::msgs::{enums::AlertLevel, message::Message}, 6 | AlertDescription, ConnectionTrafficSecrets, SupportedCipherSuite, 7 | }; 8 | 9 | pub(crate) const TLS_1_2_VERSION_NUMBER: u16 = (((ktls::TLS_1_2_VERSION_MAJOR & 0xFF) as u16) << 8) 10 | | ((ktls::TLS_1_2_VERSION_MINOR & 0xFF) as u16); 11 | 12 | pub(crate) const TLS_1_3_VERSION_NUMBER: u16 = (((ktls::TLS_1_3_VERSION_MAJOR & 0xFF) as u16) << 8) 13 | | ((ktls::TLS_1_3_VERSION_MINOR & 0xFF) as u16); 14 | 15 | /// `setsockopt` level constant: TCP 16 | const SOL_TCP: libc::c_int = 6; 17 | 18 | /// `setsockopt` SOL_TCP name constant: "upper level protocol" 19 | const TCP_ULP: libc::c_int = 31; 20 | 21 | /// `setsockopt` level constant: TLS 22 | const SOL_TLS: libc::c_int = 282; 23 | 24 | /// `setsockopt` SOL_TLS level constant: transmit (write) 25 | const TLS_TX: libc::c_int = 1; 26 | 27 | /// `setsockopt` SOL_TLS level constant: receive (read) 28 | const TLX_RX: libc::c_int = 2; 29 | 30 | pub fn setup_ulp(fd: RawFd) -> std::io::Result<()> { 31 | unsafe { 32 | if libc::setsockopt( 33 | fd, 34 | SOL_TCP, 35 | TCP_ULP, 36 | "tls".as_ptr() as *const libc::c_void, 37 | 3, 38 | ) < 0 39 | { 40 | return Err(std::io::Error::last_os_error()); 41 | } 42 | } 43 | 44 | Ok(()) 45 | } 46 | 47 | #[derive(Clone, Copy, Debug)] 48 | pub enum Direction { 49 | // Transmit 50 | Tx, 51 | // Receive 52 | Rx, 53 | } 54 | 55 | impl From for libc::c_int { 56 | fn from(val: Direction) -> Self { 57 | match val { 58 | Direction::Tx => TLS_TX, 59 | Direction::Rx => TLX_RX, 60 | } 61 | } 62 | } 63 | 64 | #[allow(dead_code)] 65 | pub enum CryptoInfo { 66 | AesGcm128(ktls::tls12_crypto_info_aes_gcm_128), 67 | AesGcm256(ktls::tls12_crypto_info_aes_gcm_256), 68 | AesCcm128(ktls::tls12_crypto_info_aes_ccm_128), 69 | Chacha20Poly1305(ktls::tls12_crypto_info_chacha20_poly1305), 70 | Sm4Gcm(ktls::tls12_crypto_info_sm4_gcm), 71 | Sm4Ccm(ktls::tls12_crypto_info_sm4_ccm), 72 | } 73 | 74 | impl CryptoInfo { 75 | /// Return the system struct as a pointer. 76 | pub fn as_ptr(&self) -> *const libc::c_void { 77 | match self { 78 | CryptoInfo::AesGcm128(info) => info as *const _ as *const libc::c_void, 79 | CryptoInfo::AesGcm256(info) => info as *const _ as *const libc::c_void, 80 | CryptoInfo::AesCcm128(info) => info as *const _ as *const libc::c_void, 81 | CryptoInfo::Chacha20Poly1305(info) => info as *const _ as *const libc::c_void, 82 | CryptoInfo::Sm4Gcm(info) => info as *const _ as *const libc::c_void, 83 | CryptoInfo::Sm4Ccm(info) => info as *const _ as *const libc::c_void, 84 | } 85 | } 86 | 87 | /// Return the system struct size. 88 | pub fn size(&self) -> usize { 89 | match self { 90 | CryptoInfo::AesGcm128(_) => std::mem::size_of::(), 91 | CryptoInfo::AesGcm256(_) => std::mem::size_of::(), 92 | CryptoInfo::AesCcm128(_) => std::mem::size_of::(), 93 | CryptoInfo::Chacha20Poly1305(_) => { 94 | std::mem::size_of::() 95 | } 96 | CryptoInfo::Sm4Gcm(_) => std::mem::size_of::(), 97 | CryptoInfo::Sm4Ccm(_) => std::mem::size_of::(), 98 | } 99 | } 100 | } 101 | 102 | #[derive(thiserror::Error, Debug)] 103 | pub enum KtlsCompatibilityError { 104 | #[error("cipher suite not supported with kTLS: {0:?}")] 105 | UnsupportedCipherSuite(SupportedCipherSuite), 106 | 107 | #[error("wrong size key")] 108 | WrongSizeKey, 109 | 110 | #[error("wrong size iv")] 111 | WrongSizeIv, 112 | } 113 | 114 | impl CryptoInfo { 115 | /// Try to convert rustls cipher suite and secrets into a `CryptoInfo`. 116 | pub fn from_rustls( 117 | cipher_suite: SupportedCipherSuite, 118 | (seq, secrets): (u64, ConnectionTrafficSecrets), 119 | ) -> Result { 120 | let version = match cipher_suite { 121 | SupportedCipherSuite::Tls12(..) => TLS_1_2_VERSION_NUMBER, 122 | SupportedCipherSuite::Tls13(..) => TLS_1_3_VERSION_NUMBER, 123 | }; 124 | 125 | Ok(match secrets { 126 | ConnectionTrafficSecrets::Aes128Gcm { key, iv } => { 127 | // see https://github.com/rustls/rustls/issues/1833, between 128 | // rustls 0.21 and 0.22, the extract_keys codepath was changed, 129 | // so, for TLS 1.2, both GCM-128 and GCM-256 return the 130 | // Aes128Gcm variant. 131 | 132 | match key.as_ref().len() { 133 | 16 => CryptoInfo::AesGcm128(ktls::tls12_crypto_info_aes_gcm_128 { 134 | info: ktls::tls_crypto_info { 135 | version, 136 | cipher_type: ktls::TLS_CIPHER_AES_GCM_128 as _, 137 | }, 138 | iv: iv 139 | .as_ref() 140 | .get(4..) 141 | .expect("AES-GCM-128 iv is 8 bytes") 142 | .try_into() 143 | .expect("AES-GCM-128 iv is 8 bytes"), 144 | key: key 145 | .as_ref() 146 | .try_into() 147 | .expect("AES-GCM-128 key is 16 bytes"), 148 | salt: iv 149 | .as_ref() 150 | .get(..4) 151 | .expect("AES-GCM-128 salt is 4 bytes") 152 | .try_into() 153 | .expect("AES-GCM-128 salt is 4 bytes"), 154 | rec_seq: seq.to_be_bytes(), 155 | }), 156 | 32 => CryptoInfo::AesGcm256(ktls::tls12_crypto_info_aes_gcm_256 { 157 | info: ktls::tls_crypto_info { 158 | version, 159 | cipher_type: ktls::TLS_CIPHER_AES_GCM_256 as _, 160 | }, 161 | iv: iv 162 | .as_ref() 163 | .get(4..) 164 | .expect("AES-GCM-256 iv is 8 bytes") 165 | .try_into() 166 | .expect("AES-GCM-256 iv is 8 bytes"), 167 | key: key 168 | .as_ref() 169 | .try_into() 170 | .expect("AES-GCM-256 key is 32 bytes"), 171 | salt: iv 172 | .as_ref() 173 | .get(..4) 174 | .expect("AES-GCM-256 salt is 4 bytes") 175 | .try_into() 176 | .expect("AES-GCM-256 salt is 4 bytes"), 177 | rec_seq: seq.to_be_bytes(), 178 | }), 179 | _ => unreachable!("GCM key length is not 16 or 32"), 180 | } 181 | } 182 | ConnectionTrafficSecrets::Aes256Gcm { key, iv } => { 183 | CryptoInfo::AesGcm256(ktls::tls12_crypto_info_aes_gcm_256 { 184 | info: ktls::tls_crypto_info { 185 | version, 186 | cipher_type: ktls::TLS_CIPHER_AES_GCM_256 as _, 187 | }, 188 | iv: iv 189 | .as_ref() 190 | .get(4..) 191 | .expect("AES-GCM-256 iv is 8 bytes") 192 | .try_into() 193 | .expect("AES-GCM-256 iv is 8 bytes"), 194 | key: key 195 | .as_ref() 196 | .try_into() 197 | .expect("AES-GCM-256 key is 32 bytes"), 198 | salt: iv 199 | .as_ref() 200 | .get(..4) 201 | .expect("AES-GCM-256 salt is 4 bytes") 202 | .try_into() 203 | .expect("AES-GCM-256 salt is 4 bytes"), 204 | rec_seq: seq.to_be_bytes(), 205 | }) 206 | } 207 | ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv } => { 208 | CryptoInfo::Chacha20Poly1305(ktls::tls12_crypto_info_chacha20_poly1305 { 209 | info: ktls::tls_crypto_info { 210 | version, 211 | cipher_type: ktls::TLS_CIPHER_CHACHA20_POLY1305 as _, 212 | }, 213 | iv: iv 214 | .as_ref() 215 | .try_into() 216 | .expect("Chacha20-Poly1305 iv is 12 bytes"), 217 | key: key 218 | .as_ref() 219 | .try_into() 220 | .expect("Chacha20-Poly1305 key is 32 bytes"), 221 | salt: ktls::__IncompleteArrayField::new(), 222 | rec_seq: seq.to_be_bytes(), 223 | }) 224 | } 225 | _ => { 226 | return Err(KtlsCompatibilityError::UnsupportedCipherSuite(cipher_suite)); 227 | } 228 | }) 229 | } 230 | } 231 | 232 | pub fn setup_tls_info(fd: RawFd, dir: Direction, info: CryptoInfo) -> Result<(), crate::Error> { 233 | let ret = unsafe { libc::setsockopt(fd, SOL_TLS, dir.into(), info.as_ptr(), info.size() as _) }; 234 | if ret < 0 { 235 | return Err(crate::Error::TlsCryptoInfoError( 236 | std::io::Error::last_os_error(), 237 | )); 238 | } 239 | Ok(()) 240 | } 241 | 242 | const TLS_SET_RECORD_TYPE: libc::c_int = 1; 243 | const ALERT: u8 = 0x15; 244 | 245 | // Yes, really. cmsg components are aligned to [libc::c_long] 246 | #[cfg_attr(target_pointer_width = "32", repr(C, align(4)))] 247 | #[cfg_attr(target_pointer_width = "64", repr(C, align(8)))] 248 | struct Cmsg { 249 | hdr: libc::cmsghdr, 250 | data: [u8; N], 251 | } 252 | 253 | impl Cmsg { 254 | fn new(level: i32, typ: i32, data: [u8; N]) -> Self { 255 | Self { 256 | hdr: libc::cmsghdr { 257 | // on Linux this is a usize, on macOS this is a u32 258 | #[allow(clippy::unnecessary_cast)] 259 | cmsg_len: (memoffset::offset_of!(Self, data) + N) as _, 260 | cmsg_level: level, 261 | cmsg_type: typ, 262 | }, 263 | data, 264 | } 265 | } 266 | } 267 | 268 | pub fn send_close_notify(fd: RawFd) -> std::io::Result<()> { 269 | let mut data = vec![]; 270 | Message::build_alert(AlertLevel::Warning, AlertDescription::CloseNotify) 271 | .payload 272 | .encode(&mut data); 273 | 274 | let mut cmsg = Cmsg::new(SOL_TLS, TLS_SET_RECORD_TYPE, [ALERT]); 275 | 276 | let msg = libc::msghdr { 277 | msg_name: std::ptr::null_mut(), 278 | msg_namelen: 0, 279 | msg_iov: &mut libc::iovec { 280 | iov_base: data.as_mut_ptr() as _, 281 | iov_len: data.len(), 282 | }, 283 | msg_iovlen: 1, 284 | msg_control: &mut cmsg as *mut _ as *mut _, 285 | msg_controllen: cmsg.hdr.cmsg_len, 286 | msg_flags: 0, 287 | }; 288 | 289 | let ret = unsafe { libc::sendmsg(fd, &msg, 0) }; 290 | if ret < 0 { 291 | return Err(std::io::Error::last_os_error()); 292 | } 293 | Ok(()) 294 | } 295 | -------------------------------------------------------------------------------- /ktls/src/ktls_stream.rs: -------------------------------------------------------------------------------- 1 | use nix::{ 2 | errno::Errno, 3 | sys::socket::{recvmsg, ControlMessageOwned, MsgFlags, SockaddrIn, TlsGetRecordType}, 4 | }; 5 | use num_enum::FromPrimitive; 6 | use std::{ 7 | io::{self, IoSliceMut}, 8 | os::unix::prelude::AsRawFd, 9 | pin::Pin, 10 | task, 11 | }; 12 | 13 | use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; 14 | 15 | use crate::AsyncReadReady; 16 | 17 | // A wrapper around `IO` that sends a `close_notify` when shut down or dropped. 18 | pin_project_lite::pin_project! { 19 | pub struct KtlsStream 20 | where 21 | IO: AsRawFd 22 | { 23 | #[pin] 24 | inner: IO, 25 | write_closed: bool, 26 | read_closed: bool, 27 | drained: Option<(usize, Vec)>, 28 | } 29 | } 30 | 31 | impl KtlsStream 32 | where 33 | IO: AsRawFd, 34 | { 35 | pub fn new(inner: IO, drained: Option>) -> Self { 36 | Self { 37 | inner, 38 | write_closed: false, 39 | read_closed: false, 40 | drained: drained.map(|drained| (0, drained)), 41 | } 42 | } 43 | 44 | /// Return the drained data + the original I/O 45 | pub fn into_raw(self) -> (Option>, IO) { 46 | (self.drained.map(|(_, drained)| drained), self.inner) 47 | } 48 | 49 | /// Returns a reference to the original I/O 50 | pub fn get_ref(&self) -> &IO { 51 | &self.inner 52 | } 53 | 54 | /// Returns a mut reference to the original I/O 55 | pub fn get_mut(&mut self) -> &mut IO { 56 | &mut self.inner 57 | } 58 | } 59 | 60 | #[derive(Debug, PartialEq, Clone, Copy, num_enum::FromPrimitive)] 61 | #[repr(u8)] 62 | enum TlsAlertLevel { 63 | Warning = 1, 64 | Fatal = 2, 65 | #[num_enum(catch_all)] 66 | Other(u8), 67 | } 68 | 69 | #[derive(Debug, PartialEq, Clone, Copy, num_enum::FromPrimitive)] 70 | #[repr(u8)] 71 | enum TlsAlertDescription { 72 | CloseNotify = 0, 73 | #[num_enum(catch_all)] 74 | Other(u8), 75 | } 76 | 77 | impl AsyncRead for KtlsStream 78 | where 79 | IO: AsRawFd + AsyncRead + AsyncReadReady, 80 | { 81 | fn poll_read( 82 | self: Pin<&mut Self>, 83 | cx: &mut task::Context<'_>, 84 | buf: &mut ReadBuf<'_>, 85 | ) -> task::Poll> { 86 | tracing::trace!(buf.remaining = %buf.remaining(), "KtlsStream::poll_read"); 87 | 88 | if self.read_closed { 89 | return task::Poll::Ready(Ok(())); 90 | } 91 | 92 | if buf.remaining() == 0 { 93 | return task::Poll::Ready(Ok(())); 94 | } 95 | 96 | let mut this = self.project(); 97 | 98 | if let Some((drain_index, drained)) = this.drained.as_mut() { 99 | let drained = &drained[*drain_index..]; 100 | let len = std::cmp::min(buf.remaining(), drained.len()); 101 | 102 | tracing::trace!(%len, "KtlsStream::poll_read, can take from drain"); 103 | buf.put_slice(&drained[..len]); 104 | 105 | *drain_index += len; 106 | if *drain_index >= drained.len() { 107 | tracing::trace!("KtlsStream::poll_read, done draining"); 108 | *this.drained = None; 109 | } 110 | cx.waker().wake_by_ref(); 111 | 112 | tracing::trace!("KtlsStream::poll_read, returning after drain"); 113 | return task::Poll::Ready(Ok(())); 114 | } 115 | 116 | let read_res = this.inner.as_mut().poll_read(cx, buf); 117 | if let task::Poll::Ready(Err(e)) = &read_res { 118 | // 5 is a generic "input/output error", it happens when 119 | // using poll_read on a kTLS socket that just received 120 | // a control message 121 | if let Some(5) = e.raw_os_error() { 122 | // could be a control message, let's check 123 | let fd = this.inner.as_raw_fd(); 124 | 125 | // XXX: recvmsg wants a `&mut Vec` so it's able to resize it 126 | // I guess? Or so there's a clear separation between uninitialized 127 | // and initialized? We could probably get read of that heap alloc, idk. 128 | 129 | // let mut cmsgspace = 130 | // [0u8; unsafe { libc::CMSG_SPACE(std::mem::size_of::() as _) as _ }]; 131 | let mut cmsgspace = Vec::with_capacity(unsafe { 132 | libc::CMSG_SPACE(std::mem::size_of::() as _) as _ 133 | }); 134 | 135 | let mut iov = [IoSliceMut::new(buf.initialize_unfilled())]; 136 | let flags = MsgFlags::empty(); 137 | 138 | let r = recvmsg::(fd, &mut iov, Some(&mut cmsgspace), flags); 139 | let r = match r { 140 | Ok(r) => r, 141 | Err(Errno::EAGAIN) => { 142 | unreachable!("expected a control message, got EAGAIN") 143 | } 144 | Err(e) => { 145 | // ok I guess it really failed then 146 | tracing::trace!(?e, "recvmsg failed"); 147 | return Err(e.into()).into(); 148 | } 149 | }; 150 | let cmsg = r 151 | .cmsgs()? 152 | .next() 153 | .expect("we should've received exactly one control message"); 154 | 155 | let record_type = match cmsg { 156 | ControlMessageOwned::TlsGetRecordType(t) => t, 157 | _ => panic!("unexpected cmsg type: {cmsg:#?}"), 158 | }; 159 | 160 | match record_type { 161 | TlsGetRecordType::ChangeCipherSpec => { 162 | panic!("change_cipher_spec isn't supported by the ktls crate") 163 | } 164 | TlsGetRecordType::Alert => { 165 | // the alert level and description are in iovs 166 | let iov = r.iovs().next().expect("expected data in iovs"); 167 | 168 | let (level, description) = match iov { 169 | [] => { 170 | // we have an early return case for that 171 | unreachable!(); 172 | } 173 | &[level] => { 174 | // https://github.com/facebookincubator/fizz/blob/fff6d9d49d3c554ab66b58822d1e1fe93e8d80f2/fizz/experimental/ktls/AsyncKTLSSocket.cpp#L144 175 | // 176 | // Since all alerts (even warning-level alerts) 177 | // signal the abort of a TLS session, we do not 178 | // need to worry about additional application 179 | // data. 180 | // 181 | // If we only have half the alert (because the 182 | // user passed a buffer of size 1), just assume 183 | // it's a close_notify 184 | ( 185 | TlsAlertLevel::from_primitive(level), 186 | TlsAlertDescription::CloseNotify, 187 | ) 188 | } 189 | &[level, description] => ( 190 | TlsAlertLevel::from_primitive(level), 191 | TlsAlertDescription::from_primitive(description), 192 | ), 193 | _ => { 194 | unreachable!( 195 | "TLS alerts are exactly 2 bytes, your kTLS is misbehaving" 196 | ); 197 | } 198 | }; 199 | 200 | match (level, description) { 201 | // https://datatracker.ietf.org/doc/html/rfc5246#section-7.2 202 | // alerts we should handle are ones with fatal level or a 203 | // close_notify 204 | (_, TlsAlertDescription::CloseNotify) | (TlsAlertLevel::Fatal, _) => { 205 | tracing::trace!(?level, ?description, "got TLS alert"); 206 | *this.read_closed = true; 207 | *this.write_closed = true; 208 | if let Err(e) = 209 | crate::ffi::send_close_notify(this.inner.as_raw_fd()) 210 | { 211 | return Err(e).into(); 212 | } 213 | // the file descriptor will be closed when the stream is dropped, 214 | // we already protect against writes-after-close_notify through 215 | // the write_closed flag 216 | return task::Poll::Ready(Ok(())); 217 | } 218 | _ => { 219 | // we got something we probably can't handle 220 | } 221 | } 222 | return task::Poll::Ready(Ok(())); 223 | } 224 | TlsGetRecordType::Handshake => { 225 | // TODO: this is where we receive TLS 1.3 resumption tickets, 226 | // should those be stored anywhere? I'm not even sure what 227 | // format they have at this point 228 | tracing::trace!( 229 | "ignoring handshake message (probably a resumption ticket)" 230 | ); 231 | } 232 | TlsGetRecordType::ApplicationData => { 233 | unreachable!("received TLS application in recvmsg, this is supposed to happen in the poll_read codepath") 234 | } 235 | TlsGetRecordType::Unknown(t) => { 236 | // just ignore the record? 237 | tracing::trace!("received record_type {t:#?}"); 238 | } 239 | _ => { 240 | tracing::trace!("received unsupported record type"); 241 | } 242 | }; 243 | 244 | // FIXME: this is hacky, but can we do better? 245 | // after we handled (..ignored) the control message, we don't 246 | // know whether the socket is still ready to be read or not. 247 | // 248 | // we could try looping (tricky code structure), but we can't, 249 | // for example, just call `poll_read`, which might fail not 250 | // with EAGAIN/EWOULDBLOCK, but because _another_ control 251 | // message is available. 252 | cx.waker().wake_by_ref(); 253 | return task::Poll::Pending; 254 | } 255 | } 256 | 257 | read_res 258 | } 259 | } 260 | 261 | impl AsyncWrite for KtlsStream 262 | where 263 | IO: AsRawFd + AsyncWrite, 264 | { 265 | fn poll_write( 266 | self: Pin<&mut Self>, 267 | cx: &mut task::Context<'_>, 268 | buf: &[u8], 269 | ) -> task::Poll> { 270 | if self.write_closed { 271 | return task::Poll::Ready(Ok(0)); 272 | } 273 | 274 | self.project().inner.poll_write(cx, buf) 275 | } 276 | 277 | fn poll_flush(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll> { 278 | self.project().inner.poll_flush(cx) 279 | } 280 | 281 | fn poll_shutdown( 282 | self: Pin<&mut Self>, 283 | cx: &mut task::Context<'_>, 284 | ) -> task::Poll> { 285 | let this = self.project(); 286 | 287 | if !*this.write_closed { 288 | // they didn't hang up on us, we're nicely being asked to shut down, 289 | // let's send a close_notify (and not wait for them to send it back) 290 | *this.write_closed = true; 291 | if let Err(e) = crate::ffi::send_close_notify(this.inner.as_raw_fd()) { 292 | return Err(e).into(); 293 | } 294 | } 295 | 296 | // this ends up closing the inner file descriptor no matter what 297 | this.inner.poll_shutdown(cx) 298 | } 299 | } 300 | 301 | impl AsRawFd for KtlsStream 302 | where 303 | IO: AsRawFd, 304 | { 305 | fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd { 306 | self.inner.as_raw_fd() 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /ktls/src/lib.rs: -------------------------------------------------------------------------------- 1 | use ffi::{setup_tls_info, setup_ulp, KtlsCompatibilityError}; 2 | use futures_util::future::try_join_all; 3 | use ktls_sys::bindings as sys; 4 | use rustls::{Connection, SupportedCipherSuite, SupportedProtocolVersion}; 5 | 6 | #[cfg(all(not(feature = "ring"), not(feature = "aws_lc_rs")))] 7 | compile_error!("This crate needs wither the 'ring' or 'aws_lc_rs' feature enabled"); 8 | #[cfg(all(feature = "ring", feature = "aws_lc_rs"))] 9 | compile_error!("The 'ring' and 'aws_lc_rs' features are mutually exclusive"); 10 | #[cfg(feature = "aws_lc_rs")] 11 | use rustls::crypto::aws_lc_rs::cipher_suite; 12 | #[cfg(feature = "ring")] 13 | use rustls::crypto::ring::cipher_suite; 14 | 15 | use smallvec::SmallVec; 16 | use std::{ 17 | future::Future, 18 | io, 19 | net::SocketAddr, 20 | os::unix::prelude::{AsRawFd, RawFd}, 21 | }; 22 | use tokio::{ 23 | io::{AsyncRead, AsyncReadExt, AsyncWrite}, 24 | net::{TcpListener, TcpStream}, 25 | }; 26 | 27 | mod ffi; 28 | pub use crate::ffi::CryptoInfo; 29 | 30 | mod async_read_ready; 31 | pub use async_read_ready::AsyncReadReady; 32 | 33 | mod ktls_stream; 34 | pub use ktls_stream::KtlsStream; 35 | 36 | mod cork_stream; 37 | pub use cork_stream::CorkStream; 38 | 39 | #[derive(Debug, Default)] 40 | pub struct CompatibleCiphers { 41 | pub tls12: CompatibleCiphersForVersion, 42 | pub tls13: CompatibleCiphersForVersion, 43 | } 44 | 45 | #[derive(Debug, Default)] 46 | pub struct CompatibleCiphersForVersion { 47 | pub aes_gcm_128: bool, 48 | pub aes_gcm_256: bool, 49 | pub chacha20_poly1305: bool, 50 | } 51 | 52 | impl CompatibleCiphers { 53 | /// List compatible ciphers. This listens on a TCP socket and blocks for a 54 | /// little while. Do once at the very start of a program. Should probably be 55 | /// behind a lazy_static / once_cell 56 | pub async fn new() -> io::Result { 57 | let mut ciphers = CompatibleCiphers::default(); 58 | 59 | let ln = TcpListener::bind("0.0.0.0:0").await?; 60 | let local_addr = ln.local_addr()?; 61 | 62 | // Accepted conns of ln 63 | let mut accepted_conns: SmallVec<[TcpStream; 12]> = SmallVec::new(); 64 | 65 | let accept_conns_fut = async { 66 | loop { 67 | if let Ok((conn, _addr)) = ln.accept().await { 68 | accepted_conns.push(conn); 69 | } 70 | } 71 | }; 72 | 73 | ciphers.test_ciphers(local_addr, accept_conns_fut).await?; 74 | 75 | Ok(ciphers) 76 | } 77 | 78 | async fn test_ciphers( 79 | &mut self, 80 | local_addr: SocketAddr, 81 | accept_conns_fut: impl Future, 82 | ) -> io::Result<()> { 83 | let ciphers: Vec<(SupportedCipherSuite, &mut bool)> = vec![ 84 | ( 85 | cipher_suite::TLS13_AES_128_GCM_SHA256, 86 | &mut self.tls13.aes_gcm_128, 87 | ), 88 | ( 89 | cipher_suite::TLS13_AES_256_GCM_SHA384, 90 | &mut self.tls13.aes_gcm_256, 91 | ), 92 | ( 93 | cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, 94 | &mut self.tls13.chacha20_poly1305, 95 | ), 96 | ( 97 | cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 98 | &mut self.tls12.aes_gcm_128, 99 | ), 100 | ( 101 | cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 102 | &mut self.tls12.aes_gcm_256, 103 | ), 104 | ( 105 | cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 106 | &mut self.tls12.chacha20_poly1305, 107 | ), 108 | ]; 109 | 110 | let create_connections_fut = 111 | try_join_all((0..ciphers.len()).map(|_| TcpStream::connect(local_addr))); 112 | 113 | let socks = tokio::select! { 114 | // Use biased here to optimize performance. 115 | // 116 | // With biased, tokio::select! would first poll create_connections_fut, 117 | // which would poll all `TcpStream::connect` futures and requests 118 | // new connections to `ln` then returns `Poll::Pending`. 119 | // 120 | // Then accept_conns_fut would be polled, which accepts all pending 121 | // connections, wake up create_connections_fut then returns 122 | // `Poll::Pending`. 123 | // 124 | // Finally, create_connections_fut wakes up and all connections 125 | // are ready, the result is collected into a Vec and ends 126 | // the tokio::select!. 127 | biased; 128 | 129 | res = create_connections_fut => res?, 130 | _ = accept_conns_fut => unreachable!(), 131 | }; 132 | 133 | assert_eq!(ciphers.len(), socks.len()); 134 | 135 | ciphers 136 | .into_iter() 137 | .zip(socks) 138 | .for_each(|((cipher_suite, field), sock)| { 139 | *field = sample_cipher_setup(&sock, cipher_suite).is_ok(); 140 | }); 141 | 142 | Ok(()) 143 | } 144 | 145 | /// Returns true if we're reasonably confident that functions like 146 | /// [config_ktls_client] and [config_ktls_server] will succeed. 147 | pub fn is_compatible(&self, suite: SupportedCipherSuite) -> bool { 148 | let kcs = match KtlsCipherSuite::try_from(suite) { 149 | Ok(kcs) => kcs, 150 | Err(_) => return false, 151 | }; 152 | 153 | let fields = match kcs.version { 154 | KtlsVersion::TLS12 => &self.tls12, 155 | KtlsVersion::TLS13 => &self.tls13, 156 | }; 157 | 158 | match kcs.typ { 159 | KtlsCipherType::AesGcm128 => fields.aes_gcm_128, 160 | KtlsCipherType::AesGcm256 => fields.aes_gcm_256, 161 | KtlsCipherType::Chacha20Poly1305 => fields.chacha20_poly1305, 162 | } 163 | } 164 | } 165 | 166 | fn sample_cipher_setup(sock: &TcpStream, cipher_suite: SupportedCipherSuite) -> Result<(), Error> { 167 | let kcs = match KtlsCipherSuite::try_from(cipher_suite) { 168 | Ok(kcs) => kcs, 169 | Err(_) => panic!("unsupported cipher suite"), 170 | }; 171 | 172 | let ffi_version = match kcs.version { 173 | KtlsVersion::TLS12 => ffi::TLS_1_2_VERSION_NUMBER, 174 | KtlsVersion::TLS13 => ffi::TLS_1_3_VERSION_NUMBER, 175 | }; 176 | 177 | let crypto_info = match kcs.typ { 178 | KtlsCipherType::AesGcm128 => CryptoInfo::AesGcm128(sys::tls12_crypto_info_aes_gcm_128 { 179 | info: sys::tls_crypto_info { 180 | version: ffi_version, 181 | cipher_type: sys::TLS_CIPHER_AES_GCM_128 as _, 182 | }, 183 | iv: Default::default(), 184 | key: Default::default(), 185 | salt: Default::default(), 186 | rec_seq: Default::default(), 187 | }), 188 | KtlsCipherType::AesGcm256 => CryptoInfo::AesGcm256(sys::tls12_crypto_info_aes_gcm_256 { 189 | info: sys::tls_crypto_info { 190 | version: ffi_version, 191 | cipher_type: sys::TLS_CIPHER_AES_GCM_256 as _, 192 | }, 193 | iv: Default::default(), 194 | key: Default::default(), 195 | salt: Default::default(), 196 | rec_seq: Default::default(), 197 | }), 198 | KtlsCipherType::Chacha20Poly1305 => { 199 | CryptoInfo::Chacha20Poly1305(sys::tls12_crypto_info_chacha20_poly1305 { 200 | info: sys::tls_crypto_info { 201 | version: ffi_version, 202 | cipher_type: sys::TLS_CIPHER_CHACHA20_POLY1305 as _, 203 | }, 204 | iv: Default::default(), 205 | key: Default::default(), 206 | salt: Default::default(), 207 | rec_seq: Default::default(), 208 | }) 209 | } 210 | }; 211 | let fd = sock.as_raw_fd(); 212 | 213 | setup_ulp(fd).map_err(Error::UlpError)?; 214 | 215 | setup_tls_info(fd, ffi::Direction::Tx, crypto_info)?; 216 | 217 | Ok(()) 218 | } 219 | 220 | #[derive(thiserror::Error, Debug)] 221 | pub enum Error { 222 | #[error("failed to enable TLS ULP (upper level protocol): {0}")] 223 | UlpError(#[source] std::io::Error), 224 | 225 | #[error("kTLS compatibility error: {0}")] 226 | KtlsCompatibility(#[from] KtlsCompatibilityError), 227 | 228 | #[error("failed to export secrets")] 229 | ExportSecrets(#[source] rustls::Error), 230 | 231 | #[error("failed to configure tx/rx (unsupported cipher?): {0}")] 232 | TlsCryptoInfoError(#[source] std::io::Error), 233 | 234 | #[error("an I/O occured while draining the rustls stream: {0}")] 235 | DrainError(#[source] std::io::Error), 236 | 237 | #[error("no negotiated cipher suite: call config_ktls_* only /after/ the handshake")] 238 | NoNegotiatedCipherSuite, 239 | } 240 | 241 | /// Configure kTLS for this socket. If this call succeeds, data can be written 242 | /// and read from this socket, and the kernel takes care of encryption 243 | /// transparently. I'm not clear how rekeying is handled (probably via control 244 | /// messages, but can't find a code sample for it). 245 | /// 246 | /// The inner IO type must be wrapped in [CorkStream] since it's the only way 247 | /// to drain a rustls stream cleanly. See its documentation for details. 248 | pub async fn config_ktls_server( 249 | mut stream: tokio_rustls::server::TlsStream>, 250 | ) -> Result, Error> 251 | where 252 | IO: AsRawFd + AsyncRead + AsyncReadReady + AsyncWrite + Unpin, 253 | { 254 | stream.get_mut().0.corked = true; 255 | let drained = drain(&mut stream).await.map_err(Error::DrainError)?; 256 | let (io, conn) = stream.into_inner(); 257 | let io = io.io; 258 | 259 | setup_inner(io.as_raw_fd(), Connection::Server(conn))?; 260 | Ok(KtlsStream::new(io, drained)) 261 | } 262 | 263 | /// Configure kTLS for this socket. If this call succeeds, data can be 264 | /// written and read from this socket, and the kernel takes care of encryption 265 | /// (and key updates, etc.) transparently. 266 | /// 267 | /// The inner IO type must be wrapped in [CorkStream] since it's the only way 268 | /// to drain a rustls stream cleanly. See its documentation for details. 269 | pub async fn config_ktls_client( 270 | mut stream: tokio_rustls::client::TlsStream>, 271 | ) -> Result, Error> 272 | where 273 | IO: AsRawFd + AsyncRead + AsyncWrite + Unpin, 274 | { 275 | stream.get_mut().0.corked = true; 276 | let drained = drain(&mut stream).await.map_err(Error::DrainError)?; 277 | let (io, conn) = stream.into_inner(); 278 | let io = io.io; 279 | 280 | setup_inner(io.as_raw_fd(), Connection::Client(conn))?; 281 | Ok(KtlsStream::new(io, drained)) 282 | } 283 | 284 | /// Read all the bytes we can read without blocking. This is used to drained the 285 | /// already-decrypted buffer from a tokio-rustls I/O type 286 | async fn drain(stream: &mut (impl AsyncRead + Unpin)) -> std::io::Result>> { 287 | tracing::trace!("Draining rustls stream"); 288 | let mut drained = vec![0u8; 128 * 1024]; 289 | let mut filled = 0; 290 | 291 | loop { 292 | tracing::trace!("stream.read called"); 293 | let n = match stream.read(&mut drained[filled..]).await { 294 | Ok(n) => n, 295 | Err(ref e) if e.kind() == std::io::ErrorKind::UnexpectedEof => { 296 | // actually this is expected for us! 297 | tracing::trace!("stream.read returned UnexpectedEof, that's expected for us"); 298 | break; 299 | } 300 | Err(e) => { 301 | tracing::trace!("stream.read returned error: {e}"); 302 | return Err(e); 303 | } 304 | }; 305 | tracing::trace!("stream.read returned {n}"); 306 | if n == 0 { 307 | // that's what CorkStream returns when it's at a message boundary 308 | break; 309 | } 310 | filled += n; 311 | } 312 | 313 | let maybe_drained = if filled == 0 { 314 | None 315 | } else { 316 | tracing::trace!("Draining rustls stream done: drained {filled} bytes"); 317 | drained.resize(filled, 0); 318 | Some(drained) 319 | }; 320 | Ok(maybe_drained) 321 | } 322 | 323 | fn setup_inner(fd: RawFd, conn: Connection) -> Result<(), Error> { 324 | let cipher_suite = match conn.negotiated_cipher_suite() { 325 | Some(cipher_suite) => cipher_suite, 326 | None => { 327 | return Err(Error::NoNegotiatedCipherSuite); 328 | } 329 | }; 330 | 331 | let secrets = match conn.dangerous_extract_secrets() { 332 | Ok(secrets) => secrets, 333 | Err(err) => return Err(Error::ExportSecrets(err)), 334 | }; 335 | 336 | ffi::setup_ulp(fd).map_err(Error::UlpError)?; 337 | 338 | let tx = CryptoInfo::from_rustls(cipher_suite, secrets.tx)?; 339 | setup_tls_info(fd, ffi::Direction::Tx, tx)?; 340 | 341 | let rx = CryptoInfo::from_rustls(cipher_suite, secrets.rx)?; 342 | setup_tls_info(fd, ffi::Direction::Rx, rx)?; 343 | 344 | Ok(()) 345 | } 346 | 347 | /// TLS versions supported by this crate 348 | #[non_exhaustive] 349 | #[derive(Debug, Clone, Copy)] 350 | pub enum KtlsVersion { 351 | TLS12, 352 | TLS13, 353 | } 354 | 355 | impl KtlsVersion { 356 | /// Converts into the equivalent rustls [SupportedProtocolVersion] 357 | pub fn as_supported_version(&self) -> &'static SupportedProtocolVersion { 358 | match self { 359 | KtlsVersion::TLS12 => &rustls::version::TLS12, 360 | KtlsVersion::TLS13 => &rustls::version::TLS13, 361 | } 362 | } 363 | } 364 | 365 | /// A TLS cipher suite. Used mostly internally. 366 | #[derive(Clone, Copy)] 367 | pub struct KtlsCipherSuite { 368 | /// The TLS version 369 | pub version: KtlsVersion, 370 | 371 | /// The cipher type 372 | pub typ: KtlsCipherType, 373 | } 374 | 375 | /// Cipher types supported by this crate 376 | #[non_exhaustive] 377 | #[derive(Debug, Clone, Copy)] 378 | pub enum KtlsCipherType { 379 | AesGcm128, 380 | AesGcm256, 381 | Chacha20Poly1305, 382 | } 383 | 384 | #[derive(Debug, thiserror::Error)] 385 | pub enum CipherSuiteError { 386 | #[error("TLS 1.2 support not built in")] 387 | Tls12NotBuiltIn, 388 | 389 | #[error("unsupported cipher suite")] 390 | UnsupportedCipherSuite(SupportedCipherSuite), 391 | } 392 | 393 | impl TryFrom for KtlsCipherSuite { 394 | type Error = CipherSuiteError; 395 | 396 | fn try_from(#[allow(unused)] suite: SupportedCipherSuite) -> Result { 397 | { 398 | let version = match suite { 399 | SupportedCipherSuite::Tls12(..) => { 400 | if !cfg!(feature = "tls12") { 401 | return Err(CipherSuiteError::Tls12NotBuiltIn); 402 | } 403 | KtlsVersion::TLS12 404 | } 405 | SupportedCipherSuite::Tls13(..) => KtlsVersion::TLS13, 406 | }; 407 | 408 | let family = { 409 | if suite == cipher_suite::TLS13_AES_128_GCM_SHA256 { 410 | KtlsCipherType::AesGcm128 411 | } else if suite == cipher_suite::TLS13_AES_256_GCM_SHA384 { 412 | KtlsCipherType::AesGcm256 413 | } else if suite == cipher_suite::TLS13_CHACHA20_POLY1305_SHA256 { 414 | KtlsCipherType::Chacha20Poly1305 415 | } else if suite == cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 { 416 | KtlsCipherType::AesGcm128 417 | } else if suite == cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 { 418 | KtlsCipherType::AesGcm256 419 | } else if suite == cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 { 420 | KtlsCipherType::Chacha20Poly1305 421 | } else { 422 | return Err(CipherSuiteError::UnsupportedCipherSuite(suite)); 423 | } 424 | }; 425 | 426 | Ok(Self { 427 | typ: family, 428 | version, 429 | }) 430 | } 431 | } 432 | } 433 | 434 | impl KtlsCipherSuite { 435 | pub fn as_supported_cipher_suite(&self) -> SupportedCipherSuite { 436 | match self.version { 437 | KtlsVersion::TLS12 => match self.typ { 438 | KtlsCipherType::AesGcm128 => cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 439 | KtlsCipherType::AesGcm256 => cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 440 | KtlsCipherType::Chacha20Poly1305 => { 441 | cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 442 | } 443 | }, 444 | KtlsVersion::TLS13 => match self.typ { 445 | KtlsCipherType::AesGcm128 => cipher_suite::TLS13_AES_128_GCM_SHA256, 446 | KtlsCipherType::AesGcm256 => cipher_suite::TLS13_AES_256_GCM_SHA384, 447 | KtlsCipherType::Chacha20Poly1305 => cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, 448 | }, 449 | } 450 | } 451 | } 452 | -------------------------------------------------------------------------------- /tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io, 3 | os::fd::{AsRawFd, RawFd}, 4 | sync::Arc, 5 | task, 6 | time::Duration, 7 | }; 8 | 9 | use ktls::{AsyncReadReady, CorkStream, KtlsCipherSuite, KtlsCipherType, KtlsVersion}; 10 | use lazy_static::lazy_static; 11 | use rcgen::generate_simple_self_signed; 12 | use rustls::{ 13 | client::Resumption, crypto::CryptoProvider, ClientConfig, RootCertStore, ServerConfig, 14 | SupportedCipherSuite, 15 | }; 16 | 17 | #[cfg(feature = "aws_lc_rs")] 18 | use rustls::crypto::aws_lc_rs::cipher_suite; 19 | #[cfg(feature = "ring")] 20 | use rustls::crypto::ring::cipher_suite; 21 | 22 | use tokio::{ 23 | io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, 24 | net::{TcpListener, TcpStream}, 25 | }; 26 | use tokio_rustls::TlsConnector; 27 | use tracing::{debug, Instrument}; 28 | use tracing_subscriber::EnvFilter; 29 | 30 | const RANDOM_SEED: u128 = 19873239487139847918274_u128; 31 | 32 | struct Payloads { 33 | client: Vec, 34 | server: Vec, 35 | } 36 | 37 | impl Default for Payloads { 38 | fn default() -> Self { 39 | let mut prng = oorandom::Rand64::new(RANDOM_SEED); 40 | let payload_len = 262_144; 41 | let mut gen_payload = || { 42 | (0..payload_len) 43 | .map(|_| (prng.rand_u64() % 256) as u8) 44 | .collect() 45 | }; 46 | 47 | Self { 48 | client: gen_payload(), 49 | server: gen_payload(), 50 | } 51 | } 52 | } 53 | 54 | lazy_static! { 55 | static ref PAYLOADS: Payloads = Payloads::default(); 56 | } 57 | 58 | fn all_suites() -> Vec { 59 | vec![ 60 | cipher_suite::TLS13_AES_128_GCM_SHA256, 61 | cipher_suite::TLS13_AES_256_GCM_SHA384, 62 | cipher_suite::TLS13_CHACHA20_POLY1305_SHA256, 63 | #[cfg(feature = "tls12")] 64 | cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 65 | #[cfg(feature = "tls12")] 66 | cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 67 | #[cfg(feature = "tls12")] 68 | cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 69 | ] 70 | } 71 | 72 | #[tokio::test] 73 | async fn compatible_ciphers() { 74 | let cc = ktls::CompatibleCiphers::new().await.unwrap(); 75 | for suite in all_suites() { 76 | assert!(cc.is_compatible(suite)); 77 | } 78 | } 79 | 80 | #[tokio::test(flavor = "current_thread")] 81 | async fn compatible_ciphers_single_thread() { 82 | let cc = ktls::CompatibleCiphers::new().await.unwrap(); 83 | for suite in all_suites() { 84 | assert!(cc.is_compatible(suite)); 85 | } 86 | } 87 | 88 | #[derive(Clone, Copy)] 89 | enum ServerTestFlavor { 90 | ClientCloses, 91 | ServerCloses, 92 | } 93 | 94 | #[test_case::test_matrix( 95 | [ 96 | KtlsVersion::TLS12, 97 | KtlsVersion::TLS13, 98 | ], 99 | [ 100 | KtlsCipherType::AesGcm128, 101 | KtlsCipherType::AesGcm256, 102 | KtlsCipherType::Chacha20Poly1305, 103 | ], 104 | [ 105 | ServerTestFlavor::ClientCloses, 106 | ServerTestFlavor::ServerCloses, 107 | ] 108 | )] 109 | #[tokio::test] 110 | async fn server_tests(version: KtlsVersion, cipher_type: KtlsCipherType, flavor: ServerTestFlavor) { 111 | if matches!(version, KtlsVersion::TLS12) && !cfg!(feature = "tls12") { 112 | println!("Skipping..."); 113 | return; 114 | } 115 | 116 | let cipher_suite = KtlsCipherSuite { 117 | version, 118 | typ: cipher_type, 119 | }; 120 | 121 | server_test_inner(cipher_suite, flavor).await 122 | } 123 | 124 | async fn server_test_inner(cipher_suite: KtlsCipherSuite, flavor: ServerTestFlavor) { 125 | tracing_subscriber::fmt() 126 | // .with_env_filter(EnvFilter::new("rustls=trace,debug")) 127 | // .with_env_filter(EnvFilter::new("debug")) 128 | .with_env_filter(EnvFilter::new("trace")) 129 | .pretty() 130 | .init(); 131 | 132 | let subject_alt_names = vec!["localhost".to_string()]; 133 | 134 | let ckey = generate_simple_self_signed(subject_alt_names).unwrap(); 135 | 136 | let mut server_config = 137 | ServerConfig::builder_with_provider(single_suite_provider(cipher_suite)) 138 | .with_protocol_versions(&[cipher_suite.version.as_supported_version()]) 139 | .unwrap() 140 | .with_no_client_auth() 141 | .with_single_cert( 142 | vec![ckey.cert.der().clone()], 143 | rustls::pki_types::PrivatePkcs8KeyDer::from(ckey.key_pair.serialize_der()).into(), 144 | ) 145 | .unwrap(); 146 | 147 | server_config.enable_secret_extraction = true; 148 | server_config.key_log = Arc::new(rustls::KeyLogFile::new()); 149 | 150 | let acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(server_config)); 151 | let ln = TcpListener::bind("[::]:0").await.unwrap(); 152 | let addr = ln.local_addr().unwrap(); 153 | 154 | let jh = tokio::spawn( 155 | async move { 156 | let (stream, addr) = ln.accept().await.unwrap(); 157 | debug!("Accepted TCP conn from {}", addr); 158 | let stream = SpyStream(stream, "server"); 159 | let stream = CorkStream::new(stream); 160 | 161 | let stream = acceptor.accept(stream).await.unwrap(); 162 | debug!("Completed TLS handshake"); 163 | 164 | // sleep for a bit to let client write more data and stress test 165 | // the draining logic 166 | tokio::time::sleep(Duration::from_millis(100)).await; 167 | 168 | let mut stream = ktls::config_ktls_server(stream).await.unwrap(); 169 | debug!("Configured kTLS"); 170 | 171 | debug!("Server reading data (1/5)"); 172 | let mut buf = vec![0u8; PAYLOADS.client.len()]; 173 | stream.read_exact(&mut buf).await.unwrap(); 174 | assert_eq!(buf, PAYLOADS.client); 175 | 176 | debug!("Server writing data (2/5)"); 177 | stream.write_all(&PAYLOADS.server).await.unwrap(); 178 | stream.flush().await.unwrap(); 179 | 180 | debug!("Server reading data (3/5)"); 181 | let mut buf = vec![0u8; PAYLOADS.client.len()]; 182 | stream.read_exact(&mut buf).await.unwrap(); 183 | assert_eq!(buf, PAYLOADS.client); 184 | 185 | debug!("Server writing data (4/5)"); 186 | stream.write_all(&PAYLOADS.server).await.unwrap(); 187 | stream.flush().await.unwrap(); 188 | 189 | match flavor { 190 | ServerTestFlavor::ClientCloses => { 191 | debug!("Server reading from closed session (5/5)"); 192 | assert!( 193 | stream.read_exact(&mut buf[..1]).await.is_err(), 194 | "Session still open?" 195 | ); 196 | } 197 | ServerTestFlavor::ServerCloses => { 198 | debug!("Server sending close notify (5/5)"); 199 | stream.shutdown().await.unwrap(); 200 | 201 | debug!("Server trying to write after closing"); 202 | stream.write_all(&PAYLOADS.server).await.unwrap_err(); 203 | } 204 | } 205 | 206 | assert_eq!(stream.get_ref().1, "server"); 207 | assert_eq!(stream.get_mut().1, "server"); 208 | assert_eq!(stream.into_raw().1 .1, "server"); 209 | } 210 | .instrument(tracing::info_span!("server")), 211 | ); 212 | 213 | let mut root_store = RootCertStore::empty(); 214 | root_store.add(ckey.cert.der().clone()).unwrap(); 215 | 216 | let client_config = ClientConfig::builder() 217 | .with_root_certificates(root_store) 218 | .with_no_client_auth(); 219 | 220 | let tls_connector = TlsConnector::from(Arc::new(client_config)); 221 | 222 | let stream = TcpStream::connect(addr).await.unwrap(); 223 | let mut stream = tls_connector 224 | .connect("localhost".try_into().unwrap(), stream) 225 | .await 226 | .unwrap(); 227 | 228 | debug!("Client writing data (1/5)"); 229 | stream.write_all(&PAYLOADS.client).await.unwrap(); 230 | debug!("Flushing"); 231 | stream.flush().await.unwrap(); 232 | 233 | debug!("Client reading data (2/5)"); 234 | let mut buf = vec![0u8; PAYLOADS.server.len()]; 235 | stream.read_exact(&mut buf).await.unwrap(); 236 | assert_eq!(buf, PAYLOADS.server); 237 | 238 | debug!("Client writing data (3/5)"); 239 | stream.write_all(&PAYLOADS.client).await.unwrap(); 240 | debug!("Flushing"); 241 | stream.flush().await.unwrap(); 242 | 243 | debug!("Client reading data (4/5)"); 244 | let mut buf = vec![0u8; PAYLOADS.server.len()]; 245 | stream.read_exact(&mut buf).await.unwrap(); 246 | assert_eq!(buf, PAYLOADS.server); 247 | 248 | match flavor { 249 | ServerTestFlavor::ClientCloses => { 250 | debug!("Client sending close notify (5/5)"); 251 | stream.shutdown().await.unwrap(); 252 | 253 | debug!("Client trying to write after closing"); 254 | stream.write_all(&PAYLOADS.client).await.unwrap_err(); 255 | } 256 | ServerTestFlavor::ServerCloses => { 257 | debug!("Client reading from closed session (5/5)"); 258 | assert!( 259 | stream.read_exact(&mut buf[..1]).await.is_err(), 260 | "Session still open?" 261 | ); 262 | } 263 | } 264 | 265 | jh.await.unwrap(); 266 | } 267 | 268 | #[test_case::test_matrix( 269 | [ 270 | KtlsVersion::TLS12, 271 | KtlsVersion::TLS13, 272 | ], 273 | [ 274 | KtlsCipherType::AesGcm128, 275 | KtlsCipherType::AesGcm256, 276 | KtlsCipherType::Chacha20Poly1305, 277 | ], 278 | [ 279 | ClientTestFlavor::ShortLastBuffer, 280 | ClientTestFlavor::LongLastBuffer, 281 | ] 282 | )] 283 | #[tokio::test] 284 | async fn client_tests(version: KtlsVersion, cipher_type: KtlsCipherType, flavor: ClientTestFlavor) { 285 | if matches!(version, KtlsVersion::TLS12) && !cfg!(feature = "tls12") { 286 | println!("Skipping..."); 287 | return; 288 | } 289 | 290 | let cipher_suite = KtlsCipherSuite { 291 | version, 292 | typ: cipher_type, 293 | }; 294 | 295 | client_test_inner(cipher_suite, flavor).await 296 | } 297 | 298 | enum ClientTestFlavor { 299 | ShortLastBuffer, 300 | LongLastBuffer, 301 | } 302 | 303 | async fn client_test_inner(cipher_suite: KtlsCipherSuite, flavor: ClientTestFlavor) { 304 | tracing_subscriber::fmt() 305 | // .with_env_filter(EnvFilter::new("rustls=trace,debug")) 306 | // .with_env_filter(EnvFilter::new("debug")) 307 | .with_env_filter(EnvFilter::new("trace")) 308 | .pretty() 309 | .init(); 310 | 311 | let subject_alt_names = vec!["localhost".to_string()]; 312 | 313 | let ckey = generate_simple_self_signed(subject_alt_names).unwrap(); 314 | 315 | let mut server_config = 316 | ServerConfig::builder_with_provider(single_suite_provider(cipher_suite)) 317 | .with_protocol_versions(&[cipher_suite.version.as_supported_version()]) 318 | .unwrap() 319 | .with_no_client_auth() 320 | .with_single_cert( 321 | vec![ckey.cert.der().clone()], 322 | rustls::pki_types::PrivatePkcs8KeyDer::from(ckey.key_pair.serialize_der()).into(), 323 | ) 324 | .unwrap(); 325 | 326 | server_config.key_log = Arc::new(rustls::KeyLogFile::new()); 327 | // server_config.send_tls13_tickets = 0; 328 | 329 | let acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(server_config)); 330 | let ln = TcpListener::bind("[::]:0").await.unwrap(); 331 | let addr = ln.local_addr().unwrap(); 332 | 333 | let jh = tokio::spawn( 334 | async move { 335 | let (stream, addr) = ln.accept().await.unwrap(); 336 | 337 | debug!("Accepted TCP conn from {}", addr); 338 | let mut stream = acceptor.accept(stream).await.unwrap(); 339 | debug!("Completed TLS handshake"); 340 | 341 | debug!("Server reading data (1/5)"); 342 | let mut buf = vec![0u8; PAYLOADS.client.len()]; 343 | stream.read_exact(&mut buf).await.unwrap(); 344 | assert_eq!(buf, PAYLOADS.client); 345 | 346 | debug!("Server writing data (2/5)"); 347 | stream.write_all(&PAYLOADS.server).await.unwrap(); 348 | 349 | debug!("Server reading data (3/5)"); 350 | let mut buf = vec![0u8; PAYLOADS.client.len()]; 351 | stream.read_exact(&mut buf).await.unwrap(); 352 | assert_eq!(buf, PAYLOADS.client); 353 | 354 | for _i in 0..3 { 355 | debug!("Making the client wait (to make busywaits REALLY obvious)"); 356 | tokio::time::sleep(Duration::from_millis(250)).await; 357 | } 358 | 359 | debug!("Server writing data (4/5)"); 360 | stream.write_all(&PAYLOADS.server).await.unwrap(); 361 | 362 | debug!("Server sending close notify (5/5)"); 363 | stream.shutdown().await.unwrap(); 364 | 365 | debug!("Server trying to write after close notify"); 366 | stream.write_all(&PAYLOADS.server).await.unwrap_err(); 367 | 368 | debug!("Server is happy with the exchange"); 369 | } 370 | .instrument(tracing::info_span!("server")), 371 | ); 372 | 373 | let mut root_store = RootCertStore::empty(); 374 | root_store.add(ckey.cert.der().clone()).unwrap(); 375 | 376 | let mut client_config = ClientConfig::builder() 377 | .with_root_certificates(root_store) 378 | .with_no_client_auth(); 379 | 380 | client_config.enable_secret_extraction = true; 381 | client_config.resumption = Resumption::disabled(); 382 | 383 | let tls_connector = TlsConnector::from(Arc::new(client_config)); 384 | 385 | let stream = TcpStream::connect(addr).await.unwrap(); 386 | let stream = CorkStream::new(stream); 387 | 388 | let stream = tls_connector 389 | .connect("localhost".try_into().unwrap(), stream) 390 | .await 391 | .unwrap(); 392 | 393 | let stream = ktls::config_ktls_client(stream).await.unwrap(); 394 | let mut stream = SpyStream(stream, "client"); 395 | 396 | debug!("Client writing data (1/5)"); 397 | stream.write_all(&PAYLOADS.client).await.unwrap(); 398 | debug!("Flushing"); 399 | stream.flush().await.unwrap(); 400 | 401 | tokio::time::sleep(Duration::from_millis(250)).await; 402 | 403 | debug!("Client reading data (2/5)"); 404 | let mut buf = vec![0u8; PAYLOADS.server.len()]; 405 | stream.read_exact(&mut buf).await.unwrap(); 406 | assert_eq!(buf, PAYLOADS.server); 407 | 408 | debug!("Client writing data (3/5)"); 409 | stream.write_all(&PAYLOADS.client).await.unwrap(); 410 | debug!("Flushing"); 411 | stream.flush().await.unwrap(); 412 | 413 | debug!("Client reading data (4/5)"); 414 | let mut buf = vec![0u8; PAYLOADS.server.len()]; 415 | stream.read_exact(&mut buf).await.unwrap(); 416 | assert_eq!(buf, PAYLOADS.server); 417 | 418 | let buf = match flavor { 419 | ClientTestFlavor::ShortLastBuffer => &mut buf[..1], 420 | ClientTestFlavor::LongLastBuffer => &mut buf[..2], 421 | }; 422 | debug!( 423 | "Client reading from closed session (with buffer of size {})", 424 | buf.len() 425 | ); 426 | assert!(stream.read_exact(buf).await.is_err(), "Session still open?"); 427 | 428 | jh.await.unwrap(); 429 | } 430 | 431 | struct SpyStream(IO, &'static str); 432 | 433 | impl AsyncRead for SpyStream 434 | where 435 | IO: AsyncRead, 436 | { 437 | fn poll_read( 438 | self: std::pin::Pin<&mut Self>, 439 | cx: &mut task::Context<'_>, 440 | buf: &mut tokio::io::ReadBuf<'_>, 441 | ) -> task::Poll> { 442 | let old_filled = buf.filled().len(); 443 | let name = self.1; 444 | let res = unsafe { 445 | let io = self.map_unchecked_mut(|s| &mut s.0); 446 | io.poll_read(cx, buf) 447 | }; 448 | 449 | match &res { 450 | task::Poll::Ready(res) => match res { 451 | Ok(_) => { 452 | let num_read = buf.filled().len() - old_filled; 453 | tracing::debug!(%name, "SpyStream read {num_read} bytes",); 454 | } 455 | Err(e) => { 456 | tracing::debug!(%name, "SpyStream read errored: {e}"); 457 | } 458 | }, 459 | task::Poll::Pending => { 460 | tracing::debug!(%name, "SpyStream read would've blocked") 461 | } 462 | } 463 | res 464 | } 465 | } 466 | 467 | impl AsyncReadReady for SpyStream 468 | where 469 | IO: AsyncReadReady, 470 | { 471 | fn poll_read_ready(&self, cx: &mut task::Context<'_>) -> task::Poll> { 472 | self.0.poll_read_ready(cx) 473 | } 474 | } 475 | 476 | impl AsyncWrite for SpyStream 477 | where 478 | IO: AsyncWrite, 479 | { 480 | fn poll_write( 481 | self: std::pin::Pin<&mut Self>, 482 | cx: &mut task::Context<'_>, 483 | buf: &[u8], 484 | ) -> task::Poll> { 485 | let res = unsafe { 486 | let io = self.map_unchecked_mut(|s| &mut s.0); 487 | io.poll_write(cx, buf) 488 | }; 489 | 490 | match &res { 491 | task::Poll::Ready(res) => match res { 492 | Ok(n) => { 493 | tracing::debug!("SpyStream wrote {n} bytes"); 494 | } 495 | Err(e) => { 496 | tracing::debug!("SpyStream writing errored: {e}"); 497 | } 498 | }, 499 | task::Poll::Pending => { 500 | tracing::debug!("SpyStream writing would've blocked") 501 | } 502 | } 503 | res 504 | } 505 | 506 | fn poll_flush( 507 | self: std::pin::Pin<&mut Self>, 508 | cx: &mut task::Context<'_>, 509 | ) -> task::Poll> { 510 | unsafe { 511 | let io = self.map_unchecked_mut(|s| &mut s.0); 512 | io.poll_flush(cx) 513 | } 514 | } 515 | 516 | fn poll_shutdown( 517 | self: std::pin::Pin<&mut Self>, 518 | cx: &mut task::Context<'_>, 519 | ) -> task::Poll> { 520 | unsafe { 521 | let io = self.map_unchecked_mut(|s| &mut s.0); 522 | io.poll_shutdown(cx) 523 | } 524 | } 525 | } 526 | 527 | impl AsRawFd for SpyStream 528 | where 529 | IO: AsRawFd, 530 | { 531 | fn as_raw_fd(&self) -> RawFd { 532 | self.0.as_raw_fd() 533 | } 534 | } 535 | 536 | fn single_suite_provider(cipher_suite: KtlsCipherSuite) -> Arc { 537 | let mut provider = { 538 | #[cfg(feature = "aws_lc_rs")] 539 | { 540 | rustls::crypto::aws_lc_rs::default_provider() 541 | } 542 | 543 | #[cfg(feature = "ring")] 544 | { 545 | rustls::crypto::ring::default_provider() 546 | } 547 | }; 548 | provider.cipher_suites.clear(); 549 | provider 550 | .cipher_suites 551 | .push(cipher_suite.as_supported_cipher_suite()); 552 | 553 | Arc::new(provider) 554 | } 555 | --------------------------------------------------------------------------------