Expand Shadowsocks runtime compatibility
This commit is contained in:
parent
d1638726ca
commit
4d1f589280
167 changed files with 57173 additions and 1640 deletions
729
third_party/rustls-fork-shadow-tls/Cargo.lock
generated
vendored
Normal file
729
third_party/rustls-fork-shadow-tls/Cargo.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,729 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"csv",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
"itoa 0.4.8",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
"plotters-svg",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters-backend"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
|
||||
|
||||
[[package]]
|
||||
name = "plotters-svg"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
|
||||
dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-fork-shadow-tls"
|
||||
version = "0.20.8"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"criterion",
|
||||
"env_logger",
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pemfile",
|
||||
"rustversion",
|
||||
"sct",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||
dependencies = [
|
||||
"half",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
dependencies = [
|
||||
"itoa 1.0.5",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
107
third_party/rustls-fork-shadow-tls/Cargo.toml
vendored
Normal file
107
third_party/rustls-fork-shadow-tls/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.57"
|
||||
name = "rustls-fork-shadow-tls"
|
||||
version = "0.20.8"
|
||||
build = "build.rs"
|
||||
autobenches = false
|
||||
description = "Rustls is a modern TLS library written in Rust."
|
||||
homepage = "https://github.com/rustls/rustls"
|
||||
readme = "README.md"
|
||||
categories = [
|
||||
"network-programming",
|
||||
"cryptography",
|
||||
]
|
||||
license = "Apache-2.0/ISC/MIT"
|
||||
repository = "https://github.com/rustls/rustls"
|
||||
resolver = "2"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = [
|
||||
"--cfg",
|
||||
"docsrs",
|
||||
]
|
||||
|
||||
[[example]]
|
||||
name = "bogo_shim"
|
||||
path = "examples/internal/bogo_shim.rs"
|
||||
required-features = [
|
||||
"dangerous_configuration",
|
||||
"quic",
|
||||
]
|
||||
|
||||
[[example]]
|
||||
name = "trytls_shim"
|
||||
path = "examples/internal/trytls_shim.rs"
|
||||
|
||||
[[example]]
|
||||
name = "bench"
|
||||
path = "examples/internal/bench.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
path = "benches/benchmarks.rs"
|
||||
harness = false
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4.4"
|
||||
optional = true
|
||||
|
||||
[dependencies.ring]
|
||||
version = "0.16.20"
|
||||
|
||||
[dependencies.sct]
|
||||
version = "0.7.0"
|
||||
|
||||
[dependencies.webpki]
|
||||
version = "0.22.0"
|
||||
features = [
|
||||
"alloc",
|
||||
"std",
|
||||
]
|
||||
|
||||
[dev-dependencies.base64]
|
||||
version = "0.13.0"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3.0"
|
||||
|
||||
[dev-dependencies.env_logger]
|
||||
version = "0.9.0"
|
||||
|
||||
[dev-dependencies.log]
|
||||
version = "0.4.4"
|
||||
|
||||
[dev-dependencies.rustls-pemfile]
|
||||
version = "1.0.0"
|
||||
|
||||
[dev-dependencies.webpki-roots]
|
||||
version = "0.22.0"
|
||||
|
||||
[build-dependencies.rustversion]
|
||||
version = "1.0.6"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
dangerous_configuration = []
|
||||
default = [
|
||||
"logging",
|
||||
"tls12",
|
||||
]
|
||||
logging = ["log"]
|
||||
quic = []
|
||||
read_buf = ["rustversion"]
|
||||
secret_extraction = []
|
||||
tls12 = []
|
||||
62
third_party/rustls-fork-shadow-tls/Cargo.toml.orig
generated
vendored
Normal file
62
third_party/rustls-fork-shadow-tls/Cargo.toml.orig
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
[package]
|
||||
name = "rustls-fork-shadow-tls"
|
||||
version = "0.20.8"
|
||||
edition = "2018"
|
||||
rust-version = "1.57"
|
||||
license = "Apache-2.0/ISC/MIT"
|
||||
readme = "../README.md"
|
||||
description = "Rustls is a modern TLS library written in Rust."
|
||||
homepage = "https://github.com/rustls/rustls"
|
||||
repository = "https://github.com/rustls/rustls"
|
||||
categories = ["network-programming", "cryptography"]
|
||||
autobenches = false
|
||||
build = "build.rs"
|
||||
resolver = "2"
|
||||
|
||||
[build-dependencies]
|
||||
rustversion = { version = "1.0.6", optional = true }
|
||||
|
||||
[dependencies]
|
||||
log = { version = "0.4.4", optional = true }
|
||||
ring = "0.16.20"
|
||||
sct = "0.7.0"
|
||||
webpki = { version = "0.22.0", features = ["alloc", "std"] }
|
||||
|
||||
[features]
|
||||
default = ["logging", "tls12"]
|
||||
logging = ["log"]
|
||||
dangerous_configuration = []
|
||||
secret_extraction = []
|
||||
quic = []
|
||||
tls12 = []
|
||||
read_buf = ["rustversion"]
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.9.0"
|
||||
log = "0.4.4"
|
||||
webpki-roots = "0.22.0"
|
||||
criterion = "0.3.0"
|
||||
rustls-pemfile = "1.0.0"
|
||||
base64 = "0.13.0"
|
||||
|
||||
[[example]]
|
||||
name = "bogo_shim"
|
||||
path = "examples/internal/bogo_shim.rs"
|
||||
required-features = ["dangerous_configuration", "quic"]
|
||||
|
||||
[[example]]
|
||||
name = "trytls_shim"
|
||||
path = "examples/internal/trytls_shim.rs"
|
||||
|
||||
[[example]]
|
||||
name = "bench"
|
||||
path = "examples/internal/bench.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
path = "benches/benchmarks.rs"
|
||||
harness = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
201
third_party/rustls-fork-shadow-tls/LICENSE-APACHE
vendored
Normal file
201
third_party/rustls-fork-shadow-tls/LICENSE-APACHE
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
15
third_party/rustls-fork-shadow-tls/LICENSE-ISC
vendored
Normal file
15
third_party/rustls-fork-shadow-tls/LICENSE-ISC
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
ISC License (ISC)
|
||||
Copyright (c) 2016, Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
25
third_party/rustls-fork-shadow-tls/LICENSE-MIT
vendored
Normal file
25
third_party/rustls-fork-shadow-tls/LICENSE-MIT
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2016 Joseph Birr-Pixton <jpixton@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
298
third_party/rustls-fork-shadow-tls/README.md
vendored
Normal file
298
third_party/rustls-fork-shadow-tls/README.md
vendored
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
<p align="center">
|
||||
<img width="460" height="300" src="https://raw.githubusercontent.com/rustls/rustls/main/admin/rustls-logo-web.png">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Rustls is a modern TLS library written in Rust. It uses <a href = "https://github.com/briansmith/ring"><em>ring</em></a> for cryptography and <a href = "https://github.com/briansmith/webpki">webpki</a> for certificate
|
||||
verification.
|
||||
</p>
|
||||
|
||||
# Status
|
||||
Rustls is ready for use. There are no major breaking interface changes
|
||||
envisioned after the set included in the 0.20 release.
|
||||
|
||||
If you'd like to help out, please see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
[](https://github.com/rustls/rustls/actions/workflows/build.yml?query=branch%3Amain)
|
||||
[](https://codecov.io/gh/rustls/rustls/)
|
||||
[](https://docs.rs/rustls/)
|
||||
[](https://discord.gg/MCSB76RU96)
|
||||
|
||||
## Release history
|
||||
|
||||
* Next release
|
||||
- Planned: removal of unused signature verification schemes at link-time.
|
||||
* 0.20.8 (2023-01-12)
|
||||
- Yield an error from `ConnectionCommon::read_tls()` if buffers are full.
|
||||
Both a full deframer buffer and a full incoming plaintext buffer will
|
||||
now cause an error to be returned. Callers should call `process_new_packets()`
|
||||
and read out the plaintext data from `reader()` after each successful call to `read_tls()`.
|
||||
- The minimum supported Rust version is now 1.57.0 due to some dependencies
|
||||
requiring it.
|
||||
* 0.20.7 (2022-10-18)
|
||||
- Expose secret extraction API under the `secret_extraction` cargo feature.
|
||||
This is designed to enable switching from rustls to kTLS (kernel TLS
|
||||
offload) after a successful TLS 1.2/1.3 handshake, for example.
|
||||
- Move filtering of signature schemes after config selection, avoiding the need
|
||||
for linking in encryption/decryption code for all cipher suites at the cost of
|
||||
exposing more signature schemes in the `ClientHello` emitted by the `Acceptor`.
|
||||
- Expose AlertDescription, ContentType, and HandshakeType,
|
||||
SignatureAlgorithm, and NamedGroup as part of the stable API. Previously they
|
||||
were part of the unstable internals API, but were referenced by parts of the
|
||||
stable API.
|
||||
- We now have a [Discord channel](https://discord.gg/MCSB76RU96) for community
|
||||
discussions.
|
||||
- The minimum supported Rust version is now 1.56.0 due to several dependencies
|
||||
requiring it.
|
||||
* 0.20.6 (2022-05-18)
|
||||
- 0.20.5 included a change to track more context for the `Error::CorruptMessage`
|
||||
which made API-incompatible changes to the `Error` type. We yanked 0.20.5
|
||||
and have reverted that change as part of 0.20.6.
|
||||
* 0.20.5 (2022-05-14)
|
||||
- Correct compatbility with servers which return no TLS extensions and take
|
||||
advantage of a special case encoding.
|
||||
- Remove spurious warn-level logging introduced in 0.20.3.
|
||||
- Expose cipher suites in `ClientHello` type.
|
||||
- Allow verification of IP addresses with `dangerous_config` enabled.
|
||||
- Retry I/O operations in `ConnectionCommon::complete_io()` when interrupted.
|
||||
- Fix server::ResolvesServerCertUsingSni case sensitivity.
|
||||
* 0.20.4 (2022-02-19)
|
||||
- Correct regression in QUIC 0-RTT support.
|
||||
* 0.20.3 (2022-02-13)
|
||||
- Support loading ECDSA keys in SEC1 format.
|
||||
- Support receipt of 0-RTT "early data" in TLS1.3 servers. It is not enabled
|
||||
by default; opt in by setting `ServerConfig::max_early_data_size` to a non-zero
|
||||
value.
|
||||
- Support sending of data with the first server flight. This is also not
|
||||
enabled by default either: opt in by setting `ServerConfig::send_half_rtt_data`.
|
||||
- Support `read_buf` interface when compiled with nightly. This means
|
||||
data can be safely read out of a rustls connection into a buffer without
|
||||
the buffer requiring initialisation first. Set the `read_buf` feature to
|
||||
use this.
|
||||
- Improve efficiency when writing vectors of TLS types.
|
||||
- Reduce copying and improve efficiency in TLS1.2 handshake.
|
||||
* 0.20.2 (2021-11-21)
|
||||
- Fix `CipherSuite::as_str()` value (as introduced in 0.20.1).
|
||||
* 0.20.1 (2021-11-14)
|
||||
- Allow cipher suite enum items to be stringified.
|
||||
- Improve documentation of configuration builder types.
|
||||
- Ensure unused cipher suites can be removed at link-time.
|
||||
- Ensure single-use error types implement `std::error::Error`, and are public.
|
||||
|
||||
See [RELEASE_NOTES.md](RELEASE_NOTES.md) for further change history.
|
||||
|
||||
# Documentation
|
||||
Lives here: https://docs.rs/rustls/
|
||||
|
||||
# Approach
|
||||
Rustls is a TLS library that aims to provide a good level of cryptographic security,
|
||||
requires no configuration to achieve that security, and provides no unsafe features or
|
||||
obsolete cryptography.
|
||||
|
||||
## Current features
|
||||
|
||||
* TLS1.2 and TLS1.3.
|
||||
* ECDSA, Ed25519 or RSA server authentication by clients.
|
||||
* ECDSA, Ed25519 or RSA server authentication by servers.
|
||||
* Forward secrecy using ECDHE; with curve25519, nistp256 or nistp384 curves.
|
||||
* AES128-GCM and AES256-GCM bulk encryption, with safe nonces.
|
||||
* ChaCha20-Poly1305 bulk encryption ([RFC7905](https://tools.ietf.org/html/rfc7905)).
|
||||
* ALPN support.
|
||||
* SNI support.
|
||||
* Tunable fragment size to make TLS messages match size of underlying transport.
|
||||
* Optional use of vectored IO to minimise system calls.
|
||||
* TLS1.2 session resumption.
|
||||
* TLS1.2 resumption via tickets ([RFC5077](https://tools.ietf.org/html/rfc5077)).
|
||||
* TLS1.3 resumption via tickets or session storage.
|
||||
* TLS1.3 0-RTT data for clients.
|
||||
* TLS1.3 0-RTT data for servers.
|
||||
* Client authentication by clients.
|
||||
* Client authentication by servers.
|
||||
* Extended master secret support ([RFC7627](https://tools.ietf.org/html/rfc7627)).
|
||||
* Exporters ([RFC5705](https://tools.ietf.org/html/rfc5705)).
|
||||
* OCSP stapling by servers.
|
||||
* SCT stapling by servers.
|
||||
* SCT verification by clients.
|
||||
|
||||
## Possible future features
|
||||
|
||||
* PSK support.
|
||||
* OCSP verification by clients.
|
||||
* Certificate pinning.
|
||||
|
||||
## Non-features
|
||||
|
||||
For reasons [explained in the manual](https://docs.rs/rustls/latest/rustls/manual/_02_tls_vulnerabilities/index.html),
|
||||
rustls does not and will not support:
|
||||
|
||||
* SSL1, SSL2, SSL3, TLS1 or TLS1.1.
|
||||
* RC4.
|
||||
* DES or triple DES.
|
||||
* EXPORT ciphersuites.
|
||||
* MAC-then-encrypt ciphersuites.
|
||||
* Ciphersuites without forward secrecy.
|
||||
* Renegotiation.
|
||||
* Kerberos.
|
||||
* Compression.
|
||||
* Discrete-log Diffie-Hellman.
|
||||
* Automatic protocol version downgrade.
|
||||
|
||||
There are plenty of other libraries that provide these features should you
|
||||
need them.
|
||||
|
||||
### Platform support
|
||||
|
||||
Rustls uses [`ring`](https://crates.io/crates/ring) for implementing the
|
||||
cryptography in TLS. As a result, rustls only runs on platforms
|
||||
[supported by `ring`](https://github.com/briansmith/ring#online-automated-testing).
|
||||
At the time of writing this means x86, x86-64, armv7, and aarch64.
|
||||
|
||||
Rustls requires Rust 1.56 or later.
|
||||
|
||||
# Example code
|
||||
There are two example programs which use
|
||||
[mio](https://github.com/carllerche/mio) to do asynchronous IO.
|
||||
|
||||
## Client example program
|
||||
The client example program is named `tlsclient-mio`. The interface looks like:
|
||||
|
||||
```tlsclient-mio
|
||||
Connects to the TLS server at hostname:PORT. The default PORT
|
||||
is 443. By default, this reads a request from stdin (to EOF)
|
||||
before making the connection. --http replaces this with a
|
||||
basic HTTP GET request for /.
|
||||
|
||||
If --cafile is not supplied, a built-in set of CA certificates
|
||||
are used from the webpki-roots crate.
|
||||
|
||||
Usage:
|
||||
tlsclient-mio [options] [--suite SUITE ...] [--proto PROTO ...] [--protover PROTOVER ...] <hostname>
|
||||
tlsclient-mio (--version | -v)
|
||||
tlsclient-mio (--help | -h)
|
||||
|
||||
Options:
|
||||
-p, --port PORT Connect to PORT [default: 443].
|
||||
--http Send a basic HTTP GET request for /.
|
||||
--cafile CAFILE Read root certificates from CAFILE.
|
||||
--auth-key KEY Read client authentication key from KEY.
|
||||
--auth-certs CERTS Read client authentication certificates from CERTS.
|
||||
CERTS must match up with KEY.
|
||||
--protover VERSION Disable default TLS version list, and use
|
||||
VERSION instead. May be used multiple times.
|
||||
--suite SUITE Disable default cipher suite list, and use
|
||||
SUITE instead. May be used multiple times.
|
||||
--proto PROTOCOL Send ALPN extension containing PROTOCOL.
|
||||
May be used multiple times to offer several protocols.
|
||||
--cache CACHE Save session cache to file CACHE.
|
||||
--no-tickets Disable session ticket support.
|
||||
--no-sni Disable server name indication support.
|
||||
--insecure Disable certificate verification.
|
||||
--verbose Emit log output.
|
||||
--max-frag-size M Limit outgoing messages to M bytes.
|
||||
--version, -v Show tool version.
|
||||
--help, -h Show this screen.
|
||||
```
|
||||
|
||||
Some sample runs:
|
||||
|
||||
```
|
||||
$ cargo run --bin tlsclient-mio -- --http mozilla-modern.badssl.com
|
||||
HTTP/1.1 200 OK
|
||||
Server: nginx/1.6.2 (Ubuntu)
|
||||
Date: Wed, 01 Jun 2016 18:44:00 GMT
|
||||
Content-Type: text/html
|
||||
Content-Length: 644
|
||||
(...)
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
$ cargo run --bin tlsclient-mio -- --http expired.badssl.com
|
||||
TLS error: WebPkiError(CertExpired, ValidateServerCert)
|
||||
Connection closed
|
||||
```
|
||||
|
||||
## Server example program
|
||||
The server example program is named `tlsserver-mio`. The interface looks like:
|
||||
|
||||
```tlsserver-mio
|
||||
Runs a TLS server on :PORT. The default PORT is 443.
|
||||
|
||||
`echo' mode means the server echoes received data on each connection.
|
||||
|
||||
`http' mode means the server blindly sends a HTTP response on each
|
||||
connection.
|
||||
|
||||
`forward' means the server forwards plaintext to a connection made to
|
||||
localhost:fport.
|
||||
|
||||
`--certs' names the full certificate chain, `--key' provides the
|
||||
RSA private key.
|
||||
|
||||
Usage:
|
||||
tlsserver-mio --certs CERTFILE --key KEYFILE [--suite SUITE ...] [--proto PROTO ...] [--protover PROTOVER ...] [options] echo
|
||||
tlsserver-mio --certs CERTFILE --key KEYFILE [--suite SUITE ...] [--proto PROTO ...] [--protover PROTOVER ...] [options] http
|
||||
tlsserver-mio --certs CERTFILE --key KEYFILE [--suite SUITE ...] [--proto PROTO ...] [--protover PROTOVER ...] [options] forward <fport>
|
||||
tlsserver-mio (--version | -v)
|
||||
tlsserver-mio (--help | -h)
|
||||
|
||||
Options:
|
||||
-p, --port PORT Listen on PORT [default: 443].
|
||||
--certs CERTFILE Read server certificates from CERTFILE.
|
||||
This should contain PEM-format certificates
|
||||
in the right order (the first certificate should
|
||||
certify KEYFILE, the last should be a root CA).
|
||||
--key KEYFILE Read private key from KEYFILE. This should be a RSA
|
||||
private key or PKCS8-encoded private key, in PEM format.
|
||||
--ocsp OCSPFILE Read DER-encoded OCSP response from OCSPFILE and staple
|
||||
to certificate. Optional.
|
||||
--auth CERTFILE Enable client authentication, and accept certificates
|
||||
signed by those roots provided in CERTFILE.
|
||||
--require-auth Send a fatal alert if the client does not complete client
|
||||
authentication.
|
||||
--resumption Support session resumption.
|
||||
--tickets Support tickets.
|
||||
--protover VERSION Disable default TLS version list, and use
|
||||
VERSION instead. May be used multiple times.
|
||||
--suite SUITE Disable default cipher suite list, and use
|
||||
SUITE instead. May be used multiple times.
|
||||
--proto PROTOCOL Negotiate PROTOCOL using ALPN.
|
||||
May be used multiple times.
|
||||
--verbose Emit log output.
|
||||
--version, -v Show tool version.
|
||||
--help, -h Show this screen.
|
||||
```
|
||||
|
||||
Here's a sample run; we start a TLS echo server, then connect to it with
|
||||
`openssl` and `tlsclient-mio`:
|
||||
|
||||
```
|
||||
$ cargo run --bin tlsserver-mio -- --certs test-ca/rsa/end.fullchain --key test-ca/rsa/end.rsa -p 8443 echo &
|
||||
$ echo hello world | openssl s_client -ign_eof -quiet -connect localhost:8443
|
||||
depth=2 CN = ponytown RSA CA
|
||||
verify error:num=19:self signed certificate in certificate chain
|
||||
hello world
|
||||
^C
|
||||
$ echo hello world | cargo run --bin tlsclient-mio -- --cafile test-ca/rsa/ca.cert -p 8443 localhost
|
||||
hello world
|
||||
^C
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
Rustls is distributed under the following three licenses:
|
||||
|
||||
- Apache License version 2.0.
|
||||
- MIT license.
|
||||
- ISC license.
|
||||
|
||||
These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC
|
||||
respectively. You may use this software under the terms of any
|
||||
of these licenses, at your option.
|
||||
|
||||
# Code of conduct
|
||||
|
||||
This project adopts the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct).
|
||||
Please email rustls-mod@googlegroups.com to report any instance of misconduct, or if you
|
||||
have any comments or questions on the Code of Conduct.
|
||||
26
third_party/rustls-fork-shadow-tls/benches/benchmarks.rs
vendored
Normal file
26
third_party/rustls-fork-shadow-tls/benches/benchmarks.rs
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
/// Microbenchmarks go here. Larger benchmarks of (e.g..) protocol
|
||||
/// performance go in examples/internal/bench.rs.
|
||||
use criterion::Criterion;
|
||||
|
||||
#[path = "../tests/common/mod.rs"]
|
||||
mod test_utils;
|
||||
use test_utils::*;
|
||||
|
||||
use rustls::ServerConnection;
|
||||
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn bench_ewouldblock(c: &mut Criterion) {
|
||||
let server_config = make_server_config(KeyType::Rsa);
|
||||
let mut server = ServerConnection::new(Arc::new(server_config)).unwrap();
|
||||
let mut read_ewouldblock = FailsReads::new(io::ErrorKind::WouldBlock);
|
||||
c.bench_function("read_tls with EWOULDBLOCK", move |b| {
|
||||
b.iter(|| server.read_tls(&mut read_ewouldblock))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_ewouldblock);
|
||||
criterion_main!(benches);
|
||||
13
third_party/rustls-fork-shadow-tls/build.rs
vendored
Normal file
13
third_party/rustls-fork-shadow-tls/build.rs
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/// This build script allows us to enable the `read_buf` language feature only
|
||||
/// for Rust Nightly.
|
||||
///
|
||||
/// See the comment in lib.rs to understand why we need this.
|
||||
|
||||
#[cfg_attr(feature = "read_buf", rustversion::not(nightly))]
|
||||
fn main() {}
|
||||
|
||||
#[cfg(feature = "read_buf")]
|
||||
#[rustversion::nightly]
|
||||
fn main() {
|
||||
println!("cargo:rustc-cfg=read_buf");
|
||||
}
|
||||
664
third_party/rustls-fork-shadow-tls/examples/internal/bench.rs
vendored
Normal file
664
third_party/rustls-fork-shadow-tls/examples/internal/bench.rs
vendored
Normal file
|
|
@ -0,0 +1,664 @@
|
|||
// This program does assorted benchmarking of rustls.
|
||||
//
|
||||
// Note: we don't use any of the standard 'cargo bench', 'test::Bencher',
|
||||
// etc. because it's unstable at the time of writing.
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rustls::client::{ClientSessionMemoryCache, NoClientSessionStorage};
|
||||
use rustls::server::{
|
||||
AllowAnyAuthenticatedClient, NoClientAuth, NoServerSessionStorage, ServerSessionMemoryCache,
|
||||
};
|
||||
use rustls::RootCertStore;
|
||||
use rustls::Ticketer;
|
||||
use rustls::{ClientConfig, ClientConnection};
|
||||
use rustls::{ConnectionCommon, SideData};
|
||||
use rustls::{ServerConfig, ServerConnection};
|
||||
|
||||
fn duration_nanos(d: Duration) -> f64 {
|
||||
(d.as_secs() as f64) + f64::from(d.subsec_nanos()) / 1e9
|
||||
}
|
||||
|
||||
fn _bench<Fsetup, Ftest, S>(count: usize, name: &'static str, f_setup: Fsetup, f_test: Ftest)
|
||||
where
|
||||
Fsetup: Fn() -> S,
|
||||
Ftest: Fn(S),
|
||||
{
|
||||
let mut times = Vec::new();
|
||||
|
||||
for _ in 0..count {
|
||||
let state = f_setup();
|
||||
let start = Instant::now();
|
||||
f_test(state);
|
||||
times.push(duration_nanos(Instant::now().duration_since(start)));
|
||||
}
|
||||
|
||||
println!("{}", name);
|
||||
println!("{:?}", times);
|
||||
}
|
||||
|
||||
fn time<F>(mut f: F) -> f64
|
||||
where
|
||||
F: FnMut(),
|
||||
{
|
||||
let start = Instant::now();
|
||||
f();
|
||||
let end = Instant::now();
|
||||
duration_nanos(end.duration_since(start))
|
||||
}
|
||||
|
||||
fn transfer<L, R, LS, RS>(left: &mut L, right: &mut R, expect_data: Option<usize>) -> f64
|
||||
where
|
||||
L: DerefMut + Deref<Target = ConnectionCommon<LS>>,
|
||||
R: DerefMut + Deref<Target = ConnectionCommon<RS>>,
|
||||
LS: SideData,
|
||||
RS: SideData,
|
||||
{
|
||||
let mut tls_buf = [0u8; 262144];
|
||||
let mut read_time = 0f64;
|
||||
let mut data_left = expect_data;
|
||||
let mut data_buf = [0u8; 8192];
|
||||
|
||||
loop {
|
||||
let mut sz = 0;
|
||||
|
||||
while left.wants_write() {
|
||||
let written = left
|
||||
.write_tls(&mut tls_buf[sz..].as_mut())
|
||||
.unwrap();
|
||||
if written == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
sz += written;
|
||||
}
|
||||
|
||||
if sz == 0 {
|
||||
return read_time;
|
||||
}
|
||||
|
||||
let mut offs = 0;
|
||||
loop {
|
||||
let start = Instant::now();
|
||||
match right.read_tls(&mut tls_buf[offs..sz].as_ref()) {
|
||||
Ok(read) => {
|
||||
right.process_new_packets().unwrap();
|
||||
offs += read;
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("error on transfer {}..{}: {}", offs, sz, err);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(left) = &mut data_left {
|
||||
loop {
|
||||
let sz = match right.reader().read(&mut data_buf) {
|
||||
Ok(sz) => sz,
|
||||
Err(err) if err.kind() == io::ErrorKind::WouldBlock => break,
|
||||
Err(err) => panic!("failed to read data: {}", err),
|
||||
};
|
||||
|
||||
*left -= sz;
|
||||
if *left == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let end = Instant::now();
|
||||
read_time += duration_nanos(end.duration_since(start));
|
||||
if sz == offs {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
enum ClientAuth {
|
||||
No,
|
||||
Yes,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
enum Resumption {
|
||||
No,
|
||||
SessionID,
|
||||
Tickets,
|
||||
}
|
||||
|
||||
impl Resumption {
|
||||
fn label(&self) -> &'static str {
|
||||
match *self {
|
||||
Resumption::No => "no-resume",
|
||||
Resumption::SessionID => "sessionid",
|
||||
Resumption::Tickets => "tickets",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copied from tests/api.rs
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
enum KeyType {
|
||||
Rsa,
|
||||
Ecdsa,
|
||||
Ed25519,
|
||||
}
|
||||
|
||||
struct BenchmarkParam {
|
||||
key_type: KeyType,
|
||||
ciphersuite: rustls::SupportedCipherSuite,
|
||||
version: &'static rustls::SupportedProtocolVersion,
|
||||
}
|
||||
|
||||
impl BenchmarkParam {
|
||||
const fn new(
|
||||
key_type: KeyType,
|
||||
ciphersuite: rustls::SupportedCipherSuite,
|
||||
version: &'static rustls::SupportedProtocolVersion,
|
||||
) -> BenchmarkParam {
|
||||
BenchmarkParam {
|
||||
key_type,
|
||||
ciphersuite,
|
||||
version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ALL_BENCHMARKS: &[BenchmarkParam] = &[
|
||||
#[cfg(feature = "tls12")]
|
||||
BenchmarkParam::new(
|
||||
KeyType::Rsa,
|
||||
rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
&rustls::version::TLS12,
|
||||
),
|
||||
#[cfg(feature = "tls12")]
|
||||
BenchmarkParam::new(
|
||||
KeyType::Ecdsa,
|
||||
rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
&rustls::version::TLS12,
|
||||
),
|
||||
#[cfg(feature = "tls12")]
|
||||
BenchmarkParam::new(
|
||||
KeyType::Rsa,
|
||||
rustls::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
&rustls::version::TLS12,
|
||||
),
|
||||
#[cfg(feature = "tls12")]
|
||||
BenchmarkParam::new(
|
||||
KeyType::Rsa,
|
||||
rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
&rustls::version::TLS12,
|
||||
),
|
||||
#[cfg(feature = "tls12")]
|
||||
BenchmarkParam::new(
|
||||
KeyType::Rsa,
|
||||
rustls::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
&rustls::version::TLS12,
|
||||
),
|
||||
#[cfg(feature = "tls12")]
|
||||
BenchmarkParam::new(
|
||||
KeyType::Ecdsa,
|
||||
rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
&rustls::version::TLS12,
|
||||
),
|
||||
#[cfg(feature = "tls12")]
|
||||
BenchmarkParam::new(
|
||||
KeyType::Ecdsa,
|
||||
rustls::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
&rustls::version::TLS12,
|
||||
),
|
||||
BenchmarkParam::new(
|
||||
KeyType::Rsa,
|
||||
rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
|
||||
&rustls::version::TLS13,
|
||||
),
|
||||
BenchmarkParam::new(
|
||||
KeyType::Rsa,
|
||||
rustls::cipher_suite::TLS13_AES_256_GCM_SHA384,
|
||||
&rustls::version::TLS13,
|
||||
),
|
||||
BenchmarkParam::new(
|
||||
KeyType::Rsa,
|
||||
rustls::cipher_suite::TLS13_AES_128_GCM_SHA256,
|
||||
&rustls::version::TLS13,
|
||||
),
|
||||
BenchmarkParam::new(
|
||||
KeyType::Ecdsa,
|
||||
rustls::cipher_suite::TLS13_AES_128_GCM_SHA256,
|
||||
&rustls::version::TLS13,
|
||||
),
|
||||
BenchmarkParam::new(
|
||||
KeyType::Ed25519,
|
||||
rustls::cipher_suite::TLS13_AES_128_GCM_SHA256,
|
||||
&rustls::version::TLS13,
|
||||
),
|
||||
];
|
||||
|
||||
impl KeyType {
|
||||
fn path_for(&self, part: &str) -> String {
|
||||
match self {
|
||||
KeyType::Rsa => format!("test-ca/rsa/{}", part),
|
||||
KeyType::Ecdsa => format!("test-ca/ecdsa/{}", part),
|
||||
KeyType::Ed25519 => format!("test-ca/eddsa/{}", part),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_chain(&self) -> Vec<rustls::Certificate> {
|
||||
rustls_pemfile::certs(&mut io::BufReader::new(
|
||||
fs::File::open(self.path_for("end.fullchain")).unwrap(),
|
||||
))
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| rustls::Certificate(v.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_key(&self) -> rustls::PrivateKey {
|
||||
rustls::PrivateKey(
|
||||
rustls_pemfile::pkcs8_private_keys(&mut io::BufReader::new(
|
||||
fs::File::open(self.path_for("end.key")).unwrap(),
|
||||
))
|
||||
.unwrap()[0]
|
||||
.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_client_chain(&self) -> Vec<rustls::Certificate> {
|
||||
rustls_pemfile::certs(&mut io::BufReader::new(
|
||||
fs::File::open(self.path_for("client.fullchain")).unwrap(),
|
||||
))
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|v| rustls::Certificate(v.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_client_key(&self) -> rustls::PrivateKey {
|
||||
rustls::PrivateKey(
|
||||
rustls_pemfile::pkcs8_private_keys(&mut io::BufReader::new(
|
||||
fs::File::open(self.path_for("client.key")).unwrap(),
|
||||
))
|
||||
.unwrap()[0]
|
||||
.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn make_server_config(
|
||||
params: &BenchmarkParam,
|
||||
client_auth: ClientAuth,
|
||||
resume: Resumption,
|
||||
max_fragment_size: Option<usize>,
|
||||
) -> ServerConfig {
|
||||
let client_auth = match client_auth {
|
||||
ClientAuth::Yes => {
|
||||
let roots = params.key_type.get_chain();
|
||||
let mut client_auth_roots = RootCertStore::empty();
|
||||
for root in roots {
|
||||
client_auth_roots.add(&root).unwrap();
|
||||
}
|
||||
AllowAnyAuthenticatedClient::new(client_auth_roots)
|
||||
}
|
||||
ClientAuth::No => NoClientAuth::new(),
|
||||
};
|
||||
|
||||
let mut cfg = ServerConfig::builder()
|
||||
.with_safe_default_cipher_suites()
|
||||
.with_safe_default_kx_groups()
|
||||
.with_protocol_versions(&[params.version])
|
||||
.unwrap()
|
||||
.with_client_cert_verifier(client_auth)
|
||||
.with_single_cert(params.key_type.get_chain(), params.key_type.get_key())
|
||||
.expect("bad certs/private key?");
|
||||
|
||||
if resume == Resumption::SessionID {
|
||||
cfg.session_storage = ServerSessionMemoryCache::new(128);
|
||||
} else if resume == Resumption::Tickets {
|
||||
cfg.ticketer = Ticketer::new().unwrap();
|
||||
} else {
|
||||
cfg.session_storage = Arc::new(NoServerSessionStorage {});
|
||||
}
|
||||
|
||||
cfg.max_fragment_size = max_fragment_size;
|
||||
cfg
|
||||
}
|
||||
|
||||
fn make_client_config(
|
||||
params: &BenchmarkParam,
|
||||
clientauth: ClientAuth,
|
||||
resume: Resumption,
|
||||
) -> ClientConfig {
|
||||
let mut root_store = RootCertStore::empty();
|
||||
let mut rootbuf =
|
||||
io::BufReader::new(fs::File::open(params.key_type.path_for("ca.cert")).unwrap());
|
||||
root_store.add_parsable_certificates(&rustls_pemfile::certs(&mut rootbuf).unwrap());
|
||||
|
||||
let cfg = ClientConfig::builder()
|
||||
.with_cipher_suites(&[params.ciphersuite])
|
||||
.with_safe_default_kx_groups()
|
||||
.with_protocol_versions(&[params.version])
|
||||
.unwrap()
|
||||
.with_root_certificates(root_store);
|
||||
|
||||
let mut cfg = if clientauth == ClientAuth::Yes {
|
||||
cfg.with_single_cert(
|
||||
params.key_type.get_client_chain(),
|
||||
params.key_type.get_client_key(),
|
||||
)
|
||||
.unwrap()
|
||||
} else {
|
||||
cfg.with_no_client_auth()
|
||||
};
|
||||
|
||||
if resume != Resumption::No {
|
||||
cfg.session_storage = ClientSessionMemoryCache::new(128);
|
||||
} else {
|
||||
cfg.session_storage = Arc::new(NoClientSessionStorage {});
|
||||
}
|
||||
|
||||
cfg
|
||||
}
|
||||
|
||||
fn apply_work_multiplier(work: u64) -> u64 {
|
||||
let mul = match env::var("BENCH_MULTIPLIER") {
|
||||
Ok(val) => val
|
||||
.parse::<f64>()
|
||||
.expect("invalid BENCH_MULTIPLIER value"),
|
||||
Err(_) => 1.,
|
||||
};
|
||||
|
||||
((work as f64) * mul).round() as u64
|
||||
}
|
||||
|
||||
fn bench_handshake(params: &BenchmarkParam, clientauth: ClientAuth, resume: Resumption) {
|
||||
let client_config = Arc::new(make_client_config(params, clientauth, resume));
|
||||
let server_config = Arc::new(make_server_config(params, clientauth, resume, None));
|
||||
|
||||
assert!(params.ciphersuite.version() == params.version);
|
||||
|
||||
let rounds = apply_work_multiplier(if resume == Resumption::No { 512 } else { 4096 });
|
||||
let mut client_time = 0f64;
|
||||
let mut server_time = 0f64;
|
||||
|
||||
for _ in 0..rounds {
|
||||
let server_name = "localhost".try_into().unwrap();
|
||||
let mut client = ClientConnection::new(Arc::clone(&client_config), server_name).unwrap();
|
||||
let mut server = ServerConnection::new(Arc::clone(&server_config)).unwrap();
|
||||
|
||||
server_time += time(|| {
|
||||
transfer(&mut client, &mut server, None);
|
||||
});
|
||||
client_time += time(|| {
|
||||
transfer(&mut server, &mut client, None);
|
||||
});
|
||||
server_time += time(|| {
|
||||
transfer(&mut client, &mut server, None);
|
||||
});
|
||||
client_time += time(|| {
|
||||
transfer(&mut server, &mut client, None);
|
||||
});
|
||||
}
|
||||
|
||||
println!(
|
||||
"handshakes\t{:?}\t{:?}\t{:?}\tclient\t{}\t{}\t{:.2}\thandshake/s",
|
||||
params.version,
|
||||
params.key_type,
|
||||
params.ciphersuite.suite(),
|
||||
if clientauth == ClientAuth::Yes {
|
||||
"mutual"
|
||||
} else {
|
||||
"server-auth"
|
||||
},
|
||||
resume.label(),
|
||||
(rounds as f64) / client_time
|
||||
);
|
||||
println!(
|
||||
"handshakes\t{:?}\t{:?}\t{:?}\tserver\t{}\t{}\t{:.2}\thandshake/s",
|
||||
params.version,
|
||||
params.key_type,
|
||||
params.ciphersuite.suite(),
|
||||
if clientauth == ClientAuth::Yes {
|
||||
"mutual"
|
||||
} else {
|
||||
"server-auth"
|
||||
},
|
||||
resume.label(),
|
||||
(rounds as f64) / server_time
|
||||
);
|
||||
}
|
||||
|
||||
fn do_handshake_step(client: &mut ClientConnection, server: &mut ServerConnection) -> bool {
|
||||
if server.is_handshaking() || client.is_handshaking() {
|
||||
transfer(client, server, None);
|
||||
transfer(server, client, None);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn do_handshake(client: &mut ClientConnection, server: &mut ServerConnection) {
|
||||
while do_handshake_step(client, server) {}
|
||||
}
|
||||
|
||||
fn bench_bulk(params: &BenchmarkParam, plaintext_size: u64, max_fragment_size: Option<usize>) {
|
||||
let client_config = Arc::new(make_client_config(params, ClientAuth::No, Resumption::No));
|
||||
let server_config = Arc::new(make_server_config(
|
||||
params,
|
||||
ClientAuth::No,
|
||||
Resumption::No,
|
||||
max_fragment_size,
|
||||
));
|
||||
|
||||
let server_name = "localhost".try_into().unwrap();
|
||||
let mut client = ClientConnection::new(client_config, server_name).unwrap();
|
||||
client.set_buffer_limit(None);
|
||||
let mut server = ServerConnection::new(Arc::clone(&server_config)).unwrap();
|
||||
server.set_buffer_limit(None);
|
||||
|
||||
do_handshake(&mut client, &mut server);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
buf.resize(plaintext_size as usize, 0u8);
|
||||
|
||||
let total_data = apply_work_multiplier(if plaintext_size < 8192 {
|
||||
64 * 1024 * 1024
|
||||
} else {
|
||||
1024 * 1024 * 1024
|
||||
});
|
||||
let rounds = total_data / plaintext_size;
|
||||
let mut time_send = 0f64;
|
||||
let mut time_recv = 0f64;
|
||||
|
||||
for _ in 0..rounds {
|
||||
time_send += time(|| {
|
||||
server.writer().write_all(&buf).unwrap();
|
||||
});
|
||||
|
||||
time_recv += transfer(&mut server, &mut client, Some(buf.len()));
|
||||
}
|
||||
|
||||
let mfs_str = format!(
|
||||
"max_fragment_size:{}",
|
||||
max_fragment_size
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| "default".to_string())
|
||||
);
|
||||
let total_mbs = ((plaintext_size * rounds) as f64) / (1024. * 1024.);
|
||||
println!(
|
||||
"bulk\t{:?}\t{:?}\t{}\tsend\t{:.2}\tMB/s",
|
||||
params.version,
|
||||
params.ciphersuite.suite(),
|
||||
mfs_str,
|
||||
total_mbs / time_send
|
||||
);
|
||||
println!(
|
||||
"bulk\t{:?}\t{:?}\t{}\trecv\t{:.2}\tMB/s",
|
||||
params.version,
|
||||
params.ciphersuite.suite(),
|
||||
mfs_str,
|
||||
total_mbs / time_recv
|
||||
);
|
||||
}
|
||||
|
||||
fn bench_memory(params: &BenchmarkParam, conn_count: u64) {
|
||||
let client_config = Arc::new(make_client_config(params, ClientAuth::No, Resumption::No));
|
||||
let server_config = Arc::new(make_server_config(
|
||||
params,
|
||||
ClientAuth::No,
|
||||
Resumption::No,
|
||||
None,
|
||||
));
|
||||
|
||||
// The target here is to end up with conn_count post-handshake
|
||||
// server and client sessions.
|
||||
let conn_count = (conn_count / 2) as usize;
|
||||
let mut servers = Vec::with_capacity(conn_count);
|
||||
let mut clients = Vec::with_capacity(conn_count);
|
||||
|
||||
for _i in 0..conn_count {
|
||||
servers.push(ServerConnection::new(Arc::clone(&server_config)).unwrap());
|
||||
let server_name = "localhost".try_into().unwrap();
|
||||
clients.push(ClientConnection::new(Arc::clone(&client_config), server_name).unwrap());
|
||||
}
|
||||
|
||||
for _step in 0..5 {
|
||||
for (client, server) in clients
|
||||
.iter_mut()
|
||||
.zip(servers.iter_mut())
|
||||
{
|
||||
do_handshake_step(client, server);
|
||||
}
|
||||
}
|
||||
|
||||
for client in clients.iter_mut() {
|
||||
client
|
||||
.writer()
|
||||
.write_all(&[0u8; 1024])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (client, server) in clients
|
||||
.iter_mut()
|
||||
.zip(servers.iter_mut())
|
||||
{
|
||||
transfer(client, server, Some(1024));
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_matching_benches(name: &str) -> Vec<&BenchmarkParam> {
|
||||
let r: Vec<&BenchmarkParam> = ALL_BENCHMARKS
|
||||
.iter()
|
||||
.filter(|params| {
|
||||
format!("{:?}", params.ciphersuite.suite()).to_lowercase() == name.to_lowercase()
|
||||
})
|
||||
.collect();
|
||||
|
||||
if r.is_empty() {
|
||||
panic!("unknown suite {:?}", name);
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
fn selected_tests(mut args: env::Args) {
|
||||
let mode = args
|
||||
.next()
|
||||
.expect("first argument must be mode");
|
||||
|
||||
match mode.as_ref() {
|
||||
"bulk" => match args.next() {
|
||||
Some(suite) => {
|
||||
let len = args
|
||||
.next()
|
||||
.map(|arg| {
|
||||
arg.parse::<u64>()
|
||||
.expect("3rd arg must be plaintext size integer")
|
||||
})
|
||||
.unwrap_or(1048576);
|
||||
let mfs = args.next().map(|arg| {
|
||||
arg.parse::<usize>()
|
||||
.expect("4th arg must be max_fragment_size integer")
|
||||
});
|
||||
for param in lookup_matching_benches(&suite).iter() {
|
||||
bench_bulk(param, len, mfs);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
panic!("bulk needs ciphersuite argument");
|
||||
}
|
||||
},
|
||||
|
||||
"handshake" | "handshake-resume" | "handshake-ticket" => match args.next() {
|
||||
Some(suite) => {
|
||||
let resume = if mode == "handshake" {
|
||||
Resumption::No
|
||||
} else if mode == "handshake-resume" {
|
||||
Resumption::SessionID
|
||||
} else {
|
||||
Resumption::Tickets
|
||||
};
|
||||
|
||||
for param in lookup_matching_benches(&suite).iter() {
|
||||
bench_handshake(param, ClientAuth::No, resume);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
panic!("handshake* needs ciphersuite argument");
|
||||
}
|
||||
},
|
||||
|
||||
"memory" => match args.next() {
|
||||
Some(suite) => {
|
||||
let count = args
|
||||
.next()
|
||||
.map(|arg| {
|
||||
arg.parse::<u64>()
|
||||
.expect("3rd arg must be connection count integer")
|
||||
})
|
||||
.unwrap_or(1000000);
|
||||
for param in lookup_matching_benches(&suite).iter() {
|
||||
bench_memory(param, count);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
panic!("memory needs ciphersuite argument");
|
||||
}
|
||||
},
|
||||
|
||||
_ => {
|
||||
panic!("unsupported mode {:?}", mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn all_tests() {
|
||||
for test in ALL_BENCHMARKS.iter() {
|
||||
bench_bulk(test, 1024 * 1024, None);
|
||||
bench_bulk(test, 1024 * 1024, Some(10000));
|
||||
bench_handshake(test, ClientAuth::No, Resumption::No);
|
||||
bench_handshake(test, ClientAuth::Yes, Resumption::No);
|
||||
bench_handshake(test, ClientAuth::No, Resumption::SessionID);
|
||||
bench_handshake(test, ClientAuth::Yes, Resumption::SessionID);
|
||||
bench_handshake(test, ClientAuth::No, Resumption::Tickets);
|
||||
bench_handshake(test, ClientAuth::Yes, Resumption::Tickets);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args();
|
||||
if args.len() > 1 {
|
||||
args.next();
|
||||
selected_tests(args);
|
||||
} else {
|
||||
all_tests();
|
||||
}
|
||||
}
|
||||
1195
third_party/rustls-fork-shadow-tls/examples/internal/bogo_shim.rs
vendored
Normal file
1195
third_party/rustls-fork-shadow-tls/examples/internal/bogo_shim.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
120
third_party/rustls-fork-shadow-tls/examples/internal/trytls_shim.rs
vendored
Normal file
120
third_party/rustls-fork-shadow-tls/examples/internal/trytls_shim.rs
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// A Rustls stub for TryTLS
|
||||
//
|
||||
// Author: Joachim Viide
|
||||
// See: https://github.com/HowNetWorks/trytls-rustls-stub
|
||||
//
|
||||
|
||||
use rustls::{ClientConfig, ClientConnection, Error, OwnedTrustAnchor, RootCertStore};
|
||||
use std::convert::TryInto;
|
||||
use std::env;
|
||||
use std::error::Error as StdError;
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
enum Verdict {
|
||||
Accept,
|
||||
Reject(Error),
|
||||
}
|
||||
|
||||
fn parse_args(args: &[String]) -> Result<(String, u16, ClientConfig), Box<dyn StdError>> {
|
||||
let mut root_store = RootCertStore::empty();
|
||||
match args.len() {
|
||||
3 => {
|
||||
root_store.add_server_trust_anchors(
|
||||
webpki_roots::TLS_SERVER_ROOTS
|
||||
.0
|
||||
.iter()
|
||||
.map(|ta| {
|
||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
ta.subject,
|
||||
ta.spki,
|
||||
ta.name_constraints,
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
4 => {
|
||||
let f = File::open(&args[3])?;
|
||||
root_store
|
||||
.add_parsable_certificates(&rustls_pemfile::certs(&mut BufReader::new(f)).unwrap());
|
||||
}
|
||||
_ => {
|
||||
return Err(From::from("Incorrect number of arguments"));
|
||||
}
|
||||
};
|
||||
let config = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
|
||||
let port = args[2].parse()?;
|
||||
Ok((args[1].clone(), port, config))
|
||||
}
|
||||
|
||||
fn communicate(
|
||||
host: String,
|
||||
port: u16,
|
||||
config: ClientConfig,
|
||||
) -> Result<Verdict, Box<dyn StdError>> {
|
||||
let server_name = host.as_str().try_into().unwrap();
|
||||
let rc_config = Arc::new(config);
|
||||
let mut client = ClientConnection::new(rc_config, server_name).unwrap();
|
||||
let mut stream = TcpStream::connect((&*host, port))?;
|
||||
|
||||
client
|
||||
.writer()
|
||||
.write_all(b"GET / HTTP/1.0\r\nConnection: close\r\nContent-Length: 0\r\n\r\n")?;
|
||||
loop {
|
||||
while client.wants_write() {
|
||||
client.write_tls(&mut stream)?;
|
||||
}
|
||||
|
||||
if client.wants_read() {
|
||||
if client.read_tls(&mut stream)? == 0 {
|
||||
return Err(From::from("Connection closed"));
|
||||
}
|
||||
|
||||
if let Err(err) = client.process_new_packets() {
|
||||
return match err {
|
||||
Error::InvalidCertificateData(_)
|
||||
| Error::InvalidCertificateSignature
|
||||
| Error::InvalidCertificateSignatureType
|
||||
| Error::InvalidCertificateEncoding
|
||||
| Error::AlertReceived(_) => Ok(Verdict::Reject(err)),
|
||||
_ => Err(From::from(format!("{:?}", err))),
|
||||
};
|
||||
}
|
||||
|
||||
if client.reader().read(&mut [0])? > 0 {
|
||||
return Ok(Verdict::Accept);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let (host, port, config) = parse_args(&args).unwrap_or_else(|err| {
|
||||
println!("Argument error: {}", err);
|
||||
process::exit(2);
|
||||
});
|
||||
|
||||
match communicate(host, port, config) {
|
||||
Ok(Verdict::Accept) => {
|
||||
println!("ACCEPT");
|
||||
process::exit(0);
|
||||
}
|
||||
Ok(Verdict::Reject(reason)) => {
|
||||
println!("{:?}", reason);
|
||||
println!("REJECT");
|
||||
process::exit(0);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("{}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
154
third_party/rustls-fork-shadow-tls/src/anchors.rs
vendored
Normal file
154
third_party/rustls-fork-shadow-tls/src/anchors.rs
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
use crate::key;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::{debug, trace};
|
||||
use crate::msgs::handshake::{DistinguishedName, DistinguishedNames};
|
||||
use crate::x509;
|
||||
|
||||
/// A trust anchor, commonly known as a "Root Certificate."
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OwnedTrustAnchor {
|
||||
subject: Vec<u8>,
|
||||
spki: Vec<u8>,
|
||||
name_constraints: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl OwnedTrustAnchor {
|
||||
/// Get a `webpki::TrustAnchor` by borrowing the owned elements.
|
||||
pub(crate) fn to_trust_anchor(&self) -> webpki::TrustAnchor {
|
||||
webpki::TrustAnchor {
|
||||
subject: &self.subject,
|
||||
spki: &self.spki,
|
||||
name_constraints: self.name_constraints.as_deref(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an `OwnedTrustAnchor` from its components.
|
||||
///
|
||||
/// All inputs are DER-encoded.
|
||||
///
|
||||
/// `subject` is the [Subject] field of the trust anchor.
|
||||
///
|
||||
/// `spki` is the [SubjectPublicKeyInfo] field of the trust anchor.
|
||||
///
|
||||
/// `name_constraints` is the [Name Constraints] to
|
||||
/// apply for this trust anchor, if any.
|
||||
///
|
||||
/// [Subject]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6
|
||||
/// [SubjectPublicKeyInfo]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.7
|
||||
/// [Name Constraints]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
|
||||
pub fn from_subject_spki_name_constraints(
|
||||
subject: impl Into<Vec<u8>>,
|
||||
spki: impl Into<Vec<u8>>,
|
||||
name_constraints: Option<impl Into<Vec<u8>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
subject: subject.into(),
|
||||
spki: spki.into(),
|
||||
name_constraints: name_constraints.map(|x| x.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the subject field.
|
||||
///
|
||||
/// This can be decoded using [x509-parser's FromDer trait](https://docs.rs/x509-parser/latest/x509_parser/traits/trait.FromDer.html).
|
||||
///
|
||||
/// ```ignore
|
||||
/// use x509_parser::traits::FromDer;
|
||||
/// println!("{}", x509_parser::x509::X509Name::from_der(anchor.subject())?.1);
|
||||
/// ```
|
||||
pub fn subject(&self) -> &[u8] {
|
||||
&self.subject
|
||||
}
|
||||
}
|
||||
|
||||
/// A container for root certificates able to provide a root-of-trust
|
||||
/// for connection authentication.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RootCertStore {
|
||||
/// The list of roots.
|
||||
pub roots: Vec<OwnedTrustAnchor>,
|
||||
}
|
||||
|
||||
impl RootCertStore {
|
||||
/// Make a new, empty `RootCertStore`.
|
||||
pub fn empty() -> Self {
|
||||
Self { roots: Vec::new() }
|
||||
}
|
||||
|
||||
/// Return true if there are no certificates.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Say how many certificates are in the container.
|
||||
pub fn len(&self) -> usize {
|
||||
self.roots.len()
|
||||
}
|
||||
|
||||
/// Return the Subject Names for certificates in the container.
|
||||
#[deprecated(since = "0.20.7", note = "Use OwnedTrustAnchor::subject() instead")]
|
||||
pub fn subjects(&self) -> DistinguishedNames {
|
||||
let mut r = DistinguishedNames::new();
|
||||
|
||||
for ota in &self.roots {
|
||||
let mut name = Vec::new();
|
||||
name.extend_from_slice(&ota.subject);
|
||||
x509::wrap_in_sequence(&mut name);
|
||||
r.push(DistinguishedName::new(name));
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
/// Add a single DER-encoded certificate to the store.
|
||||
pub fn add(&mut self, der: &key::Certificate) -> Result<(), webpki::Error> {
|
||||
let ta = webpki::TrustAnchor::try_from_cert_der(&der.0)?;
|
||||
let ota = OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
ta.subject,
|
||||
ta.spki,
|
||||
ta.name_constraints,
|
||||
);
|
||||
self.roots.push(ota);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds all the given TrustAnchors `anchors`. This does not
|
||||
/// fail.
|
||||
pub fn add_server_trust_anchors(
|
||||
&mut self,
|
||||
trust_anchors: impl Iterator<Item = OwnedTrustAnchor>,
|
||||
) {
|
||||
self.roots.extend(trust_anchors)
|
||||
}
|
||||
|
||||
/// Parse the given DER-encoded certificates and add all that can be parsed
|
||||
/// in a best-effort fashion.
|
||||
///
|
||||
/// This is because large collections of root certificates often
|
||||
/// include ancient or syntactically invalid certificates.
|
||||
///
|
||||
/// Returns the number of certificates added, and the number that were ignored.
|
||||
pub fn add_parsable_certificates(&mut self, der_certs: &[Vec<u8>]) -> (usize, usize) {
|
||||
let mut valid_count = 0;
|
||||
let mut invalid_count = 0;
|
||||
|
||||
for der_cert in der_certs {
|
||||
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
|
||||
match self.add(&key::Certificate(der_cert.clone())) {
|
||||
Ok(_) => valid_count += 1,
|
||||
Err(err) => {
|
||||
trace!("invalid cert der {:?}", der_cert);
|
||||
debug!("certificate parsing failed: {:?}", err);
|
||||
invalid_count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"add_parsable_certificates processed {} valid and {} invalid certs",
|
||||
valid_count, invalid_count
|
||||
);
|
||||
|
||||
(valid_count, invalid_count)
|
||||
}
|
||||
}
|
||||
77
third_party/rustls-fork-shadow-tls/src/bs_debug.rs
vendored
Normal file
77
third_party/rustls-fork-shadow-tls/src/bs_debug.rs
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
use std::fmt;
|
||||
|
||||
/// Alternative implementation of `fmt::Debug` for byte slice.
|
||||
///
|
||||
/// Standard `Debug` implementation for `[u8]` is comma separated
|
||||
/// list of numbers. Since large amount of byte strings are in fact
|
||||
/// ASCII strings or contain a lot of ASCII strings (e. g. HTTP),
|
||||
/// it is convenient to print strings as ASCII when possible.
|
||||
///
|
||||
/// This struct wraps `&[u8]` just to override `fmt::Debug`.
|
||||
///
|
||||
/// `BsDebug` is not a part of public API of bytes crate.
|
||||
pub(crate) struct BsDebug<'a>(pub(crate) &'a [u8]);
|
||||
|
||||
impl<'a> fmt::Debug for BsDebug<'a> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(fmt, "b\"")?;
|
||||
for &c in self.0 {
|
||||
// https://doc.rust-lang.org/reference.html#byte-escapes
|
||||
if c == b'\n' {
|
||||
write!(fmt, "\\n")?;
|
||||
} else if c == b'\r' {
|
||||
write!(fmt, "\\r")?;
|
||||
} else if c == b'\t' {
|
||||
write!(fmt, "\\t")?;
|
||||
} else if c == b'\\' || c == b'"' {
|
||||
write!(fmt, "\\{}", c as char)?;
|
||||
} else if c == b'\0' {
|
||||
write!(fmt, "\\0")?;
|
||||
// ASCII printable
|
||||
} else if (0x20..0x7f).contains(&c) {
|
||||
write!(fmt, "{}", c as char)?;
|
||||
} else {
|
||||
write!(fmt, "\\x{:02x}", c)?;
|
||||
}
|
||||
}
|
||||
write!(fmt, "\"")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::BsDebug;
|
||||
|
||||
#[test]
|
||||
fn debug() {
|
||||
let vec: Vec<_> = (0..0x100).map(|b| b as u8).collect();
|
||||
|
||||
let expected = "b\"\
|
||||
\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\
|
||||
\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\
|
||||
\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\
|
||||
\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\
|
||||
\x20!\\\"#$%&'()*+,-./0123456789:;<=>?\
|
||||
@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_\
|
||||
`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\
|
||||
\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\
|
||||
\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\
|
||||
\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\
|
||||
\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\
|
||||
\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\
|
||||
\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\
|
||||
\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\
|
||||
\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\
|
||||
\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\
|
||||
\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\
|
||||
\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\
|
||||
\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\
|
||||
\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\
|
||||
\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\
|
||||
\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\
|
||||
\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff\"";
|
||||
|
||||
assert_eq!(expected, format!("{:?}", BsDebug(&vec)));
|
||||
}
|
||||
}
|
||||
268
third_party/rustls-fork-shadow-tls/src/builder.rs
vendored
Normal file
268
third_party/rustls-fork-shadow-tls/src/builder.rs
vendored
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
use crate::error::Error;
|
||||
use crate::kx::{SupportedKxGroup, ALL_KX_GROUPS};
|
||||
use crate::suites::{SupportedCipherSuite, DEFAULT_CIPHER_SUITES};
|
||||
use crate::versions;
|
||||
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Building a [`ServerConfig`] or [`ClientConfig`] in a linker-friendly and
|
||||
/// complete way.
|
||||
///
|
||||
/// Linker-friendly: meaning unused cipher suites, protocol
|
||||
/// versions, key exchange mechanisms, etc. can be discarded
|
||||
/// by the linker as they'll be unreferenced.
|
||||
///
|
||||
/// Complete: the type system ensures all decisions required to run a
|
||||
/// server or client have been made by the time the process finishes.
|
||||
///
|
||||
/// Example, to make a [`ServerConfig`]:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use rustls::ServerConfig;
|
||||
/// # let certs = vec![];
|
||||
/// # let private_key = rustls::PrivateKey(vec![]);
|
||||
/// ServerConfig::builder()
|
||||
/// .with_safe_default_cipher_suites()
|
||||
/// .with_safe_default_kx_groups()
|
||||
/// .with_safe_default_protocol_versions()
|
||||
/// .unwrap()
|
||||
/// .with_no_client_auth()
|
||||
/// .with_single_cert(certs, private_key)
|
||||
/// .expect("bad certificate/key");
|
||||
/// ```
|
||||
///
|
||||
/// This may be shortened to:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use rustls::ServerConfig;
|
||||
/// # let certs = vec![];
|
||||
/// # let private_key = rustls::PrivateKey(vec![]);
|
||||
/// ServerConfig::builder()
|
||||
/// .with_safe_defaults()
|
||||
/// .with_no_client_auth()
|
||||
/// .with_single_cert(certs, private_key)
|
||||
/// .expect("bad certificate/key");
|
||||
/// ```
|
||||
///
|
||||
/// To make a [`ClientConfig`]:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use rustls::ClientConfig;
|
||||
/// # let root_certs = rustls::RootCertStore::empty();
|
||||
/// # let certs = vec![];
|
||||
/// # let private_key = rustls::PrivateKey(vec![]);
|
||||
/// ClientConfig::builder()
|
||||
/// .with_safe_default_cipher_suites()
|
||||
/// .with_safe_default_kx_groups()
|
||||
/// .with_safe_default_protocol_versions()
|
||||
/// .unwrap()
|
||||
/// .with_root_certificates(root_certs)
|
||||
/// .with_single_cert(certs, private_key)
|
||||
/// .expect("bad certificate/key");
|
||||
/// ```
|
||||
///
|
||||
/// This may be shortened to:
|
||||
///
|
||||
/// ```
|
||||
/// # use rustls::ClientConfig;
|
||||
/// # let root_certs = rustls::RootCertStore::empty();
|
||||
/// ClientConfig::builder()
|
||||
/// .with_safe_defaults()
|
||||
/// .with_root_certificates(root_certs)
|
||||
/// .with_no_client_auth();
|
||||
/// ```
|
||||
///
|
||||
/// The types used here fit together like this:
|
||||
///
|
||||
/// 1. Call [`ClientConfig::builder()`] or [`ServerConfig::builder()`] to initialize a builder.
|
||||
/// 1. You must make a decision on which cipher suites to use, typically
|
||||
/// by calling [`ConfigBuilder<S, WantsCipherSuites>::with_safe_default_cipher_suites()`].
|
||||
/// 2. Now you must make a decision
|
||||
/// on key exchange groups: typically by calling
|
||||
/// [`ConfigBuilder<S, WantsKxGroups>::with_safe_default_kx_groups()`].
|
||||
/// 3. Now you must make
|
||||
/// a decision on which protocol versions to support, typically by calling
|
||||
/// [`ConfigBuilder<S, WantsVersions>::with_safe_default_protocol_versions()`].
|
||||
/// 5. Now see [`ConfigBuilder<ClientConfig, WantsVerifier>`] or
|
||||
/// [`ConfigBuilder<ServerConfig, WantsVerifier>`] for further steps.
|
||||
///
|
||||
/// [`ServerConfig`]: crate::ServerConfig
|
||||
/// [`ClientConfig`]: crate::ClientConfig
|
||||
/// [`ClientConfig::builder()`]: crate::ClientConfig::builder()
|
||||
/// [`ServerConfig::builder()`]: crate::ServerConfig::builder()
|
||||
/// [`ConfigBuilder<ClientConfig, WantsVerifier>`]: struct.ConfigBuilder.html#impl-3
|
||||
/// [`ConfigBuilder<ServerConfig, WantsVerifier>`]: struct.ConfigBuilder.html#impl-6
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigBuilder<Side: ConfigSide, State> {
|
||||
pub(crate) state: State,
|
||||
pub(crate) side: PhantomData<Side>,
|
||||
}
|
||||
|
||||
impl<Side: ConfigSide, State: fmt::Debug> fmt::Debug for ConfigBuilder<Side, State> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let side_name = std::any::type_name::<Side>();
|
||||
let side_name = side_name
|
||||
.split("::")
|
||||
.last()
|
||||
.unwrap_or(side_name);
|
||||
f.debug_struct(&format!("ConfigBuilder<{}, _>", side_name))
|
||||
.field("state", &self.state)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Config builder state where the caller must supply cipher suites.
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WantsCipherSuites(pub(crate) ());
|
||||
|
||||
impl<S: ConfigSide> ConfigBuilder<S, WantsCipherSuites> {
|
||||
/// Start side-specific config with defaults for underlying cryptography.
|
||||
///
|
||||
/// If used, this will enable all safe supported cipher suites ([`DEFAULT_CIPHER_SUITES`]), all
|
||||
/// safe supported key exchange groups ([`ALL_KX_GROUPS`]) and all safe supported protocol
|
||||
/// versions ([`DEFAULT_VERSIONS`]).
|
||||
///
|
||||
/// These are safe defaults, useful for 99% of applications.
|
||||
///
|
||||
/// [`DEFAULT_VERSIONS`]: versions::DEFAULT_VERSIONS
|
||||
pub fn with_safe_defaults(self) -> ConfigBuilder<S, WantsVerifier> {
|
||||
ConfigBuilder {
|
||||
state: WantsVerifier {
|
||||
cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(),
|
||||
kx_groups: ALL_KX_GROUPS.to_vec(),
|
||||
versions: versions::EnabledVersions::new(versions::DEFAULT_VERSIONS),
|
||||
},
|
||||
side: self.side,
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose a specific set of cipher suites.
|
||||
pub fn with_cipher_suites(
|
||||
self,
|
||||
cipher_suites: &[SupportedCipherSuite],
|
||||
) -> ConfigBuilder<S, WantsKxGroups> {
|
||||
ConfigBuilder {
|
||||
state: WantsKxGroups {
|
||||
cipher_suites: cipher_suites.to_vec(),
|
||||
},
|
||||
side: self.side,
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose the default set of cipher suites ([`DEFAULT_CIPHER_SUITES`]).
|
||||
///
|
||||
/// Note that this default provides only high-quality suites: there is no need
|
||||
/// to filter out low-, export- or NULL-strength cipher suites: rustls does not
|
||||
/// implement these.
|
||||
pub fn with_safe_default_cipher_suites(self) -> ConfigBuilder<S, WantsKxGroups> {
|
||||
self.with_cipher_suites(DEFAULT_CIPHER_SUITES)
|
||||
}
|
||||
}
|
||||
|
||||
/// Config builder state where the caller must supply key exchange groups.
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WantsKxGroups {
|
||||
cipher_suites: Vec<SupportedCipherSuite>,
|
||||
}
|
||||
|
||||
impl<S: ConfigSide> ConfigBuilder<S, WantsKxGroups> {
|
||||
/// Choose a specific set of key exchange groups.
|
||||
pub fn with_kx_groups(
|
||||
self,
|
||||
kx_groups: &[&'static SupportedKxGroup],
|
||||
) -> ConfigBuilder<S, WantsVersions> {
|
||||
ConfigBuilder {
|
||||
state: WantsVersions {
|
||||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: kx_groups.to_vec(),
|
||||
},
|
||||
side: self.side,
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose the default set of key exchange groups ([`ALL_KX_GROUPS`]).
|
||||
///
|
||||
/// This is a safe default: rustls doesn't implement any poor-quality groups.
|
||||
pub fn with_safe_default_kx_groups(self) -> ConfigBuilder<S, WantsVersions> {
|
||||
self.with_kx_groups(&ALL_KX_GROUPS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Config builder state where the caller must supply TLS protocol versions.
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WantsVersions {
|
||||
cipher_suites: Vec<SupportedCipherSuite>,
|
||||
kx_groups: Vec<&'static SupportedKxGroup>,
|
||||
}
|
||||
|
||||
impl<S: ConfigSide> ConfigBuilder<S, WantsVersions> {
|
||||
/// Accept the default protocol versions: both TLS1.2 and TLS1.3 are enabled.
|
||||
pub fn with_safe_default_protocol_versions(
|
||||
self,
|
||||
) -> Result<ConfigBuilder<S, WantsVerifier>, Error> {
|
||||
self.with_protocol_versions(versions::DEFAULT_VERSIONS)
|
||||
}
|
||||
|
||||
/// Use a specific set of protocol versions.
|
||||
pub fn with_protocol_versions(
|
||||
self,
|
||||
versions: &[&'static versions::SupportedProtocolVersion],
|
||||
) -> Result<ConfigBuilder<S, WantsVerifier>, Error> {
|
||||
let mut any_usable_suite = false;
|
||||
for suite in &self.state.cipher_suites {
|
||||
if versions.contains(&suite.version()) {
|
||||
any_usable_suite = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !any_usable_suite {
|
||||
return Err(Error::General("no usable cipher suites configured".into()));
|
||||
}
|
||||
|
||||
if self.state.kx_groups.is_empty() {
|
||||
return Err(Error::General("no kx groups configured".into()));
|
||||
}
|
||||
|
||||
Ok(ConfigBuilder {
|
||||
state: WantsVerifier {
|
||||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: self.state.kx_groups,
|
||||
versions: versions::EnabledVersions::new(versions),
|
||||
},
|
||||
side: self.side,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Config builder state where the caller must supply a verifier.
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WantsVerifier {
|
||||
pub(crate) cipher_suites: Vec<SupportedCipherSuite>,
|
||||
pub(crate) kx_groups: Vec<&'static SupportedKxGroup>,
|
||||
pub(crate) versions: versions::EnabledVersions,
|
||||
}
|
||||
|
||||
/// Helper trait to abstract [`ConfigBuilder`] over building a [`ClientConfig`] or [`ServerConfig`].
|
||||
///
|
||||
/// [`ClientConfig`]: crate::ClientConfig
|
||||
/// [`ServerConfig`]: crate::ServerConfig
|
||||
pub trait ConfigSide: sealed::Sealed {}
|
||||
|
||||
impl ConfigSide for crate::ClientConfig {}
|
||||
impl ConfigSide for crate::ServerConfig {}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
impl Sealed for crate::ClientConfig {}
|
||||
impl Sealed for crate::ServerConfig {}
|
||||
}
|
||||
77
third_party/rustls-fork-shadow-tls/src/check.rs
vendored
Normal file
77
third_party/rustls-fork-shadow-tls/src/check.rs
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
use crate::error::Error;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::warn;
|
||||
use crate::msgs::enums::{ContentType, HandshakeType};
|
||||
use crate::msgs::message::MessagePayload;
|
||||
|
||||
/// For a Message $m, and a HandshakePayload enum member $payload_type,
|
||||
/// return Ok(payload) if $m is both a handshake message and one that
|
||||
/// has the given $payload_type. If not, return Err(rustls::Error) quoting
|
||||
/// $handshake_type as the expected handshake type.
|
||||
macro_rules! require_handshake_msg(
|
||||
( $m:expr, $handshake_type:path, $payload_type:path ) => (
|
||||
match &$m.payload {
|
||||
MessagePayload::Handshake { parsed: $crate::msgs::handshake::HandshakeMessagePayload {
|
||||
payload: $payload_type(hm),
|
||||
..
|
||||
}, .. } => Ok(hm),
|
||||
payload => Err($crate::check::inappropriate_handshake_message(
|
||||
payload,
|
||||
&[$crate::msgs::enums::ContentType::Handshake],
|
||||
&[$handshake_type]))
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// Like require_handshake_msg, but moves the payload out of $m.
|
||||
#[cfg(feature = "tls12")]
|
||||
macro_rules! require_handshake_msg_move(
|
||||
( $m:expr, $handshake_type:path, $payload_type:path ) => (
|
||||
match $m.payload {
|
||||
MessagePayload::Handshake { parsed: $crate::msgs::handshake::HandshakeMessagePayload {
|
||||
payload: $payload_type(hm),
|
||||
..
|
||||
}, .. } => Ok(hm),
|
||||
payload =>
|
||||
Err($crate::check::inappropriate_handshake_message(
|
||||
&payload,
|
||||
&[$crate::msgs::enums::ContentType::Handshake],
|
||||
&[$handshake_type]))
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
pub(crate) fn inappropriate_message(
|
||||
payload: &MessagePayload,
|
||||
content_types: &[ContentType],
|
||||
) -> Error {
|
||||
warn!(
|
||||
"Received a {:?} message while expecting {:?}",
|
||||
payload.content_type(),
|
||||
content_types
|
||||
);
|
||||
Error::InappropriateMessage {
|
||||
expect_types: content_types.to_vec(),
|
||||
got_type: payload.content_type(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn inappropriate_handshake_message(
|
||||
payload: &MessagePayload,
|
||||
content_types: &[ContentType],
|
||||
handshake_types: &[HandshakeType],
|
||||
) -> Error {
|
||||
match payload {
|
||||
MessagePayload::Handshake { parsed, .. } => {
|
||||
warn!(
|
||||
"Received a {:?} handshake message while expecting {:?}",
|
||||
parsed.typ, handshake_types
|
||||
);
|
||||
Error::InappropriateHandshakeMessage {
|
||||
expect_types: handshake_types.to_vec(),
|
||||
got_type: parsed.typ,
|
||||
}
|
||||
}
|
||||
payload => inappropriate_message(payload, content_types),
|
||||
}
|
||||
}
|
||||
101
third_party/rustls-fork-shadow-tls/src/cipher.rs
vendored
Normal file
101
third_party/rustls-fork-shadow-tls/src/cipher.rs
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
use crate::error::Error;
|
||||
use crate::msgs::codec;
|
||||
use crate::msgs::message::{BorrowedPlainMessage, OpaqueMessage, PlainMessage};
|
||||
|
||||
use ring::{aead, hkdf};
|
||||
|
||||
/// Objects with this trait can decrypt TLS messages.
|
||||
pub trait MessageDecrypter: Send + Sync {
|
||||
/// Perform the decryption over the concerned TLS message.
|
||||
|
||||
fn decrypt(&self, m: OpaqueMessage, seq: u64) -> Result<PlainMessage, Error>;
|
||||
}
|
||||
|
||||
/// Objects with this trait can encrypt TLS messages.
|
||||
pub(crate) trait MessageEncrypter: Send + Sync {
|
||||
fn encrypt(&self, m: BorrowedPlainMessage, seq: u64) -> Result<OpaqueMessage, Error>;
|
||||
}
|
||||
|
||||
impl dyn MessageEncrypter {
|
||||
pub(crate) fn invalid() -> Box<dyn MessageEncrypter> {
|
||||
Box::new(InvalidMessageEncrypter {})
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn MessageDecrypter {
|
||||
pub(crate) fn invalid() -> Box<dyn MessageDecrypter> {
|
||||
Box::new(InvalidMessageDecrypter {})
|
||||
}
|
||||
}
|
||||
|
||||
/// A write or read IV.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Iv(pub(crate) [u8; ring::aead::NONCE_LEN]);
|
||||
|
||||
impl Iv {
|
||||
#[cfg(feature = "tls12")]
|
||||
fn new(value: [u8; ring::aead::NONCE_LEN]) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
pub(crate) fn copy(value: &[u8]) -> Self {
|
||||
debug_assert_eq!(value.len(), ring::aead::NONCE_LEN);
|
||||
let mut iv = Self::new(Default::default());
|
||||
iv.0.copy_from_slice(value);
|
||||
iv
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn value(&self) -> &[u8; 12] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct IvLen;
|
||||
|
||||
impl hkdf::KeyType for IvLen {
|
||||
fn len(&self) -> usize {
|
||||
aead::NONCE_LEN
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hkdf::Okm<'_, IvLen>> for Iv {
|
||||
fn from(okm: hkdf::Okm<IvLen>) -> Self {
|
||||
let mut r = Self(Default::default());
|
||||
okm.fill(&mut r.0[..]).unwrap();
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_nonce(iv: &Iv, seq: u64) -> ring::aead::Nonce {
|
||||
let mut nonce = [0u8; ring::aead::NONCE_LEN];
|
||||
codec::put_u64(seq, &mut nonce[4..]);
|
||||
|
||||
nonce
|
||||
.iter_mut()
|
||||
.zip(iv.0.iter())
|
||||
.for_each(|(nonce, iv)| {
|
||||
*nonce ^= *iv;
|
||||
});
|
||||
|
||||
aead::Nonce::assume_unique_for_key(nonce)
|
||||
}
|
||||
|
||||
/// A `MessageEncrypter` which doesn't work.
|
||||
struct InvalidMessageEncrypter {}
|
||||
|
||||
impl MessageEncrypter for InvalidMessageEncrypter {
|
||||
fn encrypt(&self, _m: BorrowedPlainMessage, _seq: u64) -> Result<OpaqueMessage, Error> {
|
||||
Err(Error::General("encrypt not yet available".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// A `MessageDecrypter` which doesn't work.
|
||||
struct InvalidMessageDecrypter {}
|
||||
|
||||
impl MessageDecrypter for InvalidMessageDecrypter {
|
||||
fn decrypt(&self, _m: OpaqueMessage, _seq: u64) -> Result<PlainMessage, Error> {
|
||||
Err(Error::DecryptError)
|
||||
}
|
||||
}
|
||||
192
third_party/rustls-fork-shadow-tls/src/client/builder.rs
vendored
Normal file
192
third_party/rustls-fork-shadow-tls/src/client/builder.rs
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
use crate::anchors;
|
||||
use crate::builder::{ConfigBuilder, WantsVerifier};
|
||||
use crate::client::handy;
|
||||
use crate::client::{ClientConfig, ResolvesClientCert};
|
||||
use crate::error::Error;
|
||||
use crate::key;
|
||||
use crate::kx::SupportedKxGroup;
|
||||
use crate::suites::SupportedCipherSuite;
|
||||
use crate::verify::{self, CertificateTransparencyPolicy};
|
||||
use crate::versions;
|
||||
use crate::NoKeyLog;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
|
||||
impl ConfigBuilder<ClientConfig, WantsVerifier> {
|
||||
/// Choose how to verify client certificates.
|
||||
pub fn with_root_certificates(
|
||||
self,
|
||||
root_store: anchors::RootCertStore,
|
||||
) -> ConfigBuilder<ClientConfig, WantsTransparencyPolicyOrClientCert> {
|
||||
ConfigBuilder {
|
||||
state: WantsTransparencyPolicyOrClientCert {
|
||||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: self.state.kx_groups,
|
||||
versions: self.state.versions,
|
||||
root_store,
|
||||
},
|
||||
side: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dangerous_configuration")]
|
||||
/// Set a custom certificate verifier.
|
||||
pub fn with_custom_certificate_verifier(
|
||||
self,
|
||||
verifier: Arc<dyn verify::ServerCertVerifier>,
|
||||
) -> ConfigBuilder<ClientConfig, WantsClientCert> {
|
||||
ConfigBuilder {
|
||||
state: WantsClientCert {
|
||||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: self.state.kx_groups,
|
||||
versions: self.state.versions,
|
||||
verifier,
|
||||
},
|
||||
side: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A config builder state where the caller needs to supply a certificate transparency policy or
|
||||
/// client certificate resolver.
|
||||
///
|
||||
/// In this state, the caller can optionally enable certificate transparency, or ignore CT and
|
||||
/// invoke one of the methods related to client certificates (as in the [`WantsClientCert`] state).
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WantsTransparencyPolicyOrClientCert {
|
||||
cipher_suites: Vec<SupportedCipherSuite>,
|
||||
kx_groups: Vec<&'static SupportedKxGroup>,
|
||||
versions: versions::EnabledVersions,
|
||||
root_store: anchors::RootCertStore,
|
||||
}
|
||||
|
||||
impl ConfigBuilder<ClientConfig, WantsTransparencyPolicyOrClientCert> {
|
||||
/// Set Certificate Transparency logs to use for server certificate validation.
|
||||
///
|
||||
/// Because Certificate Transparency logs are sharded on a per-year basis and can be trusted or
|
||||
/// distrusted relatively quickly, rustls stores a validation deadline. Server certificates will
|
||||
/// be validated against the configured CT logs until the deadline expires. After the deadline,
|
||||
/// certificates will no longer be validated, and a warning message will be logged. The deadline
|
||||
/// may vary depending on how often you deploy builds with updated dependencies.
|
||||
pub fn with_certificate_transparency_logs(
|
||||
self,
|
||||
logs: &'static [&'static sct::Log],
|
||||
validation_deadline: SystemTime,
|
||||
) -> ConfigBuilder<ClientConfig, WantsClientCert> {
|
||||
self.with_logs(Some(CertificateTransparencyPolicy::new(
|
||||
logs,
|
||||
validation_deadline,
|
||||
)))
|
||||
}
|
||||
|
||||
/// Sets a single certificate chain and matching private key for use
|
||||
/// in client authentication.
|
||||
///
|
||||
/// `cert_chain` is a vector of DER-encoded certificates.
|
||||
/// `key_der` is a DER-encoded RSA, ECDSA, or Ed25519 private key.
|
||||
///
|
||||
/// This function fails if `key_der` is invalid.
|
||||
pub fn with_single_cert(
|
||||
self,
|
||||
cert_chain: Vec<key::Certificate>,
|
||||
key_der: key::PrivateKey,
|
||||
) -> Result<ClientConfig, Error> {
|
||||
self.with_logs(None)
|
||||
.with_single_cert(cert_chain, key_der)
|
||||
}
|
||||
|
||||
/// Do not support client auth.
|
||||
pub fn with_no_client_auth(self) -> ClientConfig {
|
||||
self.with_logs(None)
|
||||
.with_client_cert_resolver(Arc::new(handy::FailResolveClientCert {}))
|
||||
}
|
||||
|
||||
/// Sets a custom [`ResolvesClientCert`].
|
||||
pub fn with_client_cert_resolver(
|
||||
self,
|
||||
client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
|
||||
) -> ClientConfig {
|
||||
self.with_logs(None)
|
||||
.with_client_cert_resolver(client_auth_cert_resolver)
|
||||
}
|
||||
|
||||
fn with_logs(
|
||||
self,
|
||||
ct_policy: Option<CertificateTransparencyPolicy>,
|
||||
) -> ConfigBuilder<ClientConfig, WantsClientCert> {
|
||||
ConfigBuilder {
|
||||
state: WantsClientCert {
|
||||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: self.state.kx_groups,
|
||||
versions: self.state.versions,
|
||||
verifier: Arc::new(verify::WebPkiVerifier::new(
|
||||
self.state.root_store,
|
||||
ct_policy,
|
||||
)),
|
||||
},
|
||||
side: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A config builder state where the caller needs to supply whether and how to provide a client
|
||||
/// certificate.
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WantsClientCert {
|
||||
cipher_suites: Vec<SupportedCipherSuite>,
|
||||
kx_groups: Vec<&'static SupportedKxGroup>,
|
||||
versions: versions::EnabledVersions,
|
||||
verifier: Arc<dyn verify::ServerCertVerifier>,
|
||||
}
|
||||
|
||||
impl ConfigBuilder<ClientConfig, WantsClientCert> {
|
||||
/// Sets a single certificate chain and matching private key for use
|
||||
/// in client authentication.
|
||||
///
|
||||
/// `cert_chain` is a vector of DER-encoded certificates.
|
||||
/// `key_der` is a DER-encoded RSA, ECDSA, or Ed25519 private key.
|
||||
///
|
||||
/// This function fails if `key_der` is invalid.
|
||||
pub fn with_single_cert(
|
||||
self,
|
||||
cert_chain: Vec<key::Certificate>,
|
||||
key_der: key::PrivateKey,
|
||||
) -> Result<ClientConfig, Error> {
|
||||
let resolver = handy::AlwaysResolvesClientCert::new(cert_chain, &key_der)?;
|
||||
Ok(self.with_client_cert_resolver(Arc::new(resolver)))
|
||||
}
|
||||
|
||||
/// Do not support client auth.
|
||||
pub fn with_no_client_auth(self) -> ClientConfig {
|
||||
self.with_client_cert_resolver(Arc::new(handy::FailResolveClientCert {}))
|
||||
}
|
||||
|
||||
/// Sets a custom [`ResolvesClientCert`].
|
||||
pub fn with_client_cert_resolver(
|
||||
self,
|
||||
client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
|
||||
) -> ClientConfig {
|
||||
ClientConfig {
|
||||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: self.state.kx_groups,
|
||||
alpn_protocols: Vec::new(),
|
||||
session_storage: handy::ClientSessionMemoryCache::new(256),
|
||||
max_fragment_size: None,
|
||||
client_auth_cert_resolver,
|
||||
enable_tickets: true,
|
||||
versions: self.state.versions,
|
||||
enable_sni: true,
|
||||
verifier: self.state.verifier,
|
||||
key_log: Arc::new(NoKeyLog {}),
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
enable_secret_extraction: false,
|
||||
enable_early_data: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
742
third_party/rustls-fork-shadow-tls/src/client/client_conn.rs
vendored
Normal file
742
third_party/rustls-fork-shadow-tls/src/client/client_conn.rs
vendored
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
use crate::builder::{ConfigBuilder, WantsCipherSuites};
|
||||
use crate::conn::{CommonState, ConnectionCommon, Protocol, Side};
|
||||
use crate::enums::{CipherSuite, ProtocolVersion, SignatureScheme};
|
||||
use crate::error::Error;
|
||||
use crate::kx;
|
||||
use crate::kx::SupportedKxGroup;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::trace;
|
||||
#[cfg(feature = "quic")]
|
||||
use crate::msgs::enums::AlertDescription;
|
||||
use crate::msgs::handshake::ClientExtension;
|
||||
use crate::sign;
|
||||
use crate::suites::SupportedCipherSuite;
|
||||
use crate::verify;
|
||||
use crate::versions;
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
use crate::ExtractedSecrets;
|
||||
use crate::KeyLog;
|
||||
|
||||
use super::hs;
|
||||
#[cfg(feature = "quic")]
|
||||
use crate::quic;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error as StdError;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::IpAddr;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Callback used to replace the ClientHello session ID from the encoded ClientHello.
|
||||
pub type ClientSessionIdGenerator = Arc<dyn Fn(&[u8]) -> [u8; 32] + Send + Sync + 'static>;
|
||||
/// Callback used to replace a TLS 1.2 ClientHello session ID from eager key material.
|
||||
pub type ClientTls12SessionIdGenerator =
|
||||
Arc<dyn Fn(&[u8], &[Vec<u8>]) -> [u8; 32] + Send + Sync + 'static>;
|
||||
|
||||
/// Session ID generators for custom ClientHello authentication schemes.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ClientSessionIdGenerators {
|
||||
/// Generator for TLS 1.3 ClientHello authentication.
|
||||
pub tls13: Option<ClientSessionIdGenerator>,
|
||||
/// Generator for TLS 1.2 ClientHello authentication.
|
||||
pub tls12: Option<ClientTls12SessionIdGenerator>,
|
||||
}
|
||||
use std::{fmt, io, mem};
|
||||
|
||||
/// A trait for the ability to store client session data.
|
||||
/// The keys and values are opaque.
|
||||
///
|
||||
/// Both the keys and values should be treated as
|
||||
/// **highly sensitive data**, containing enough key material
|
||||
/// to break all security of the corresponding session.
|
||||
///
|
||||
/// `put` is a mutating operation; this isn't expressed
|
||||
/// in the type system to allow implementations freedom in
|
||||
/// how to achieve interior mutability. `Mutex` is a common
|
||||
/// choice.
|
||||
pub trait StoresClientSessions: Send + Sync {
|
||||
/// Stores a new `value` for `key`. Returns `true`
|
||||
/// if the value was stored.
|
||||
fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool;
|
||||
|
||||
/// Returns the latest value for `key`. Returns `None`
|
||||
/// if there's no such value.
|
||||
fn get(&self, key: &[u8]) -> Option<Vec<u8>>;
|
||||
}
|
||||
|
||||
/// A trait for the ability to choose a certificate chain and
|
||||
/// private key for the purposes of client authentication.
|
||||
pub trait ResolvesClientCert: Send + Sync {
|
||||
/// With the server-supplied acceptable issuers in `acceptable_issuers`,
|
||||
/// the server's supported signature schemes in `sigschemes`,
|
||||
/// return a certificate chain and signing key to authenticate.
|
||||
///
|
||||
/// `acceptable_issuers` is undecoded and unverified by the rustls
|
||||
/// library, but it should be expected to contain a DER encodings
|
||||
/// of X501 NAMEs.
|
||||
///
|
||||
/// Return None to continue the handshake without any client
|
||||
/// authentication. The server may reject the handshake later
|
||||
/// if it requires authentication.
|
||||
fn resolve(
|
||||
&self,
|
||||
acceptable_issuers: &[&[u8]],
|
||||
sigschemes: &[SignatureScheme],
|
||||
) -> Option<Arc<sign::CertifiedKey>>;
|
||||
|
||||
/// Return true if any certificates at all are available.
|
||||
fn has_certs(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Common configuration for (typically) all connections made by
|
||||
/// a program.
|
||||
///
|
||||
/// Making one of these can be expensive, and should be
|
||||
/// once per process rather than once per connection.
|
||||
///
|
||||
/// These must be created via the [`ClientConfig::builder()`] function.
|
||||
///
|
||||
/// # Defaults
|
||||
///
|
||||
/// * [`ClientConfig::max_fragment_size`]: the default is `None`: TLS packets are not fragmented to a specific size.
|
||||
/// * [`ClientConfig::session_storage`]: the default stores 256 sessions in memory.
|
||||
/// * [`ClientConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated.
|
||||
/// * [`ClientConfig::key_log`]: key material is not logged.
|
||||
#[derive(Clone)]
|
||||
pub struct ClientConfig {
|
||||
/// List of ciphersuites, in preference order.
|
||||
pub(super) cipher_suites: Vec<SupportedCipherSuite>,
|
||||
|
||||
/// List of supported key exchange algorithms, in preference order -- the
|
||||
/// first element is the highest priority.
|
||||
///
|
||||
/// The first element in this list is the _default key share algorithm_,
|
||||
/// and in TLS1.3 a key share for it is sent in the client hello.
|
||||
pub(super) kx_groups: Vec<&'static SupportedKxGroup>,
|
||||
|
||||
/// Which ALPN protocols we include in our client hello.
|
||||
/// If empty, no ALPN extension is sent.
|
||||
pub alpn_protocols: Vec<Vec<u8>>,
|
||||
|
||||
/// How we store session data or tickets.
|
||||
pub session_storage: Arc<dyn StoresClientSessions>,
|
||||
|
||||
/// The maximum size of TLS message we'll emit. If None, we don't limit TLS
|
||||
/// message lengths except to the 2**16 limit specified in the standard.
|
||||
///
|
||||
/// rustls enforces an arbitrary minimum of 32 bytes for this field.
|
||||
/// Out of range values are reported as errors from ClientConnection::new.
|
||||
///
|
||||
/// Setting this value to the TCP MSS may improve latency for stream-y workloads.
|
||||
pub max_fragment_size: Option<usize>,
|
||||
|
||||
/// How to decide what client auth certificate/keys to use.
|
||||
pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
|
||||
|
||||
/// Whether to support RFC5077 tickets. You must provide a working
|
||||
/// `session_storage` member for this to have any meaningful
|
||||
/// effect.
|
||||
///
|
||||
/// The default is true.
|
||||
pub enable_tickets: bool,
|
||||
|
||||
/// Supported versions, in no particular order. The default
|
||||
/// is all supported versions.
|
||||
pub(super) versions: versions::EnabledVersions,
|
||||
|
||||
/// Whether to send the Server Name Indication (SNI) extension
|
||||
/// during the client handshake.
|
||||
///
|
||||
/// The default is true.
|
||||
pub enable_sni: bool,
|
||||
|
||||
/// How to verify the server certificate chain.
|
||||
pub(super) verifier: Arc<dyn verify::ServerCertVerifier>,
|
||||
|
||||
/// How to output key material for debugging. The default
|
||||
/// does nothing.
|
||||
pub key_log: Arc<dyn KeyLog>,
|
||||
|
||||
/// Allows traffic secrets to be extracted after the handshake,
|
||||
/// e.g. for kTLS setup.
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub enable_secret_extraction: bool,
|
||||
|
||||
/// Whether to send data on the first flight ("early data") in
|
||||
/// TLS 1.3 handshakes.
|
||||
///
|
||||
/// The default is false.
|
||||
pub enable_early_data: bool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ClientConfig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ClientConfig")
|
||||
.field("alpn_protocols", &self.alpn_protocols)
|
||||
.field("max_fragment_size", &self.max_fragment_size)
|
||||
.field("enable_tickets", &self.enable_tickets)
|
||||
.field("enable_sni", &self.enable_sni)
|
||||
.field("enable_early_data", &self.enable_early_data)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientConfig {
|
||||
/// Create a builder to build up the client configuration.
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
pub fn builder() -> ConfigBuilder<Self, WantsCipherSuites> {
|
||||
ConfigBuilder {
|
||||
state: WantsCipherSuites(()),
|
||||
side: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// We support a given TLS version if it's quoted in the configured
|
||||
/// versions *and* at least one ciphersuite for this version is
|
||||
/// also configured.
|
||||
pub fn supports_version(&self, v: ProtocolVersion) -> bool {
|
||||
self.versions.contains(v)
|
||||
&& self
|
||||
.cipher_suites
|
||||
.iter()
|
||||
.any(|cs| cs.version().version == v)
|
||||
}
|
||||
|
||||
/// Access configuration options whose use is dangerous and requires
|
||||
/// extra care.
|
||||
#[cfg(feature = "dangerous_configuration")]
|
||||
pub fn dangerous(&mut self) -> danger::DangerousClientConfig {
|
||||
danger::DangerousClientConfig { cfg: self }
|
||||
}
|
||||
|
||||
pub(super) fn find_cipher_suite(&self, suite: CipherSuite) -> Option<SupportedCipherSuite> {
|
||||
self.cipher_suites
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|&scs| scs.suite() == suite)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes ways a client can know the expected name of the server.
|
||||
///
|
||||
/// This currently covers knowing the DNS name of the server, but
|
||||
/// will be extended in the future to supporting privacy-preserving names
|
||||
/// for the server ("ECH"). For this reason this enum is `non_exhaustive`.
|
||||
///
|
||||
/// # Making one
|
||||
///
|
||||
/// If you have a DNS name as a `&str`, this type implements `TryFrom<&str>`,
|
||||
/// so you can do:
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::{TryInto, TryFrom};
|
||||
/// # use rustls::ServerName;
|
||||
/// ServerName::try_from("example.com").expect("invalid DNS name");
|
||||
///
|
||||
/// // or, alternatively...
|
||||
///
|
||||
/// let x = "example.com".try_into().expect("invalid DNS name");
|
||||
/// # let _: ServerName = x;
|
||||
/// ```
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum ServerName {
|
||||
/// The server is identified by a DNS name. The name
|
||||
/// is sent in the TLS Server Name Indication (SNI)
|
||||
/// extension.
|
||||
DnsName(verify::DnsName),
|
||||
|
||||
/// The server is identified by an IP address. SNI is not
|
||||
/// done.
|
||||
IpAddress(IpAddr),
|
||||
}
|
||||
|
||||
impl ServerName {
|
||||
/// Return the name that should go in the SNI extension.
|
||||
/// If [`None`] is returned, the SNI extension is not included
|
||||
/// in the handshake.
|
||||
pub(crate) fn for_sni(&self) -> Option<webpki::DnsNameRef> {
|
||||
match self {
|
||||
Self::DnsName(dns_name) => Some(dns_name.0.as_ref()),
|
||||
Self::IpAddress(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a prefix-free, unique encoding for the name.
|
||||
pub(crate) fn encode(&self) -> Vec<u8> {
|
||||
enum UniqueTypeCode {
|
||||
DnsName = 0x01,
|
||||
IpAddr = 0x02,
|
||||
}
|
||||
|
||||
match self {
|
||||
Self::DnsName(dns_name) => {
|
||||
let bytes = dns_name.0.as_ref();
|
||||
|
||||
let mut r = Vec::with_capacity(2 + bytes.as_ref().len());
|
||||
r.push(UniqueTypeCode::DnsName as u8);
|
||||
r.push(bytes.as_ref().len() as u8);
|
||||
r.extend_from_slice(bytes.as_ref());
|
||||
|
||||
r
|
||||
}
|
||||
Self::IpAddress(address) => {
|
||||
let string = address.to_string();
|
||||
let bytes = string.as_bytes();
|
||||
|
||||
let mut r = Vec::with_capacity(2 + bytes.len());
|
||||
r.push(UniqueTypeCode::IpAddr as u8);
|
||||
r.push(bytes.len() as u8);
|
||||
r.extend_from_slice(bytes);
|
||||
|
||||
r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to make a ServerName from a string by parsing
|
||||
/// it as a DNS name.
|
||||
impl TryFrom<&str> for ServerName {
|
||||
type Error = InvalidDnsNameError;
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
match webpki::DnsNameRef::try_from_ascii_str(s) {
|
||||
Ok(dns) => Ok(Self::DnsName(verify::DnsName(dns.into()))),
|
||||
Err(webpki::InvalidDnsNameError) => match s.parse() {
|
||||
Ok(ip) => Ok(Self::IpAddress(ip)),
|
||||
Err(_) => Err(InvalidDnsNameError),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The provided input could not be parsed because
|
||||
/// it is not a syntactically-valid DNS Name.
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidDnsNameError;
|
||||
|
||||
impl fmt::Display for InvalidDnsNameError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("invalid dns name")
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for InvalidDnsNameError {}
|
||||
|
||||
/// Container for unsafe APIs
|
||||
#[cfg(feature = "dangerous_configuration")]
|
||||
pub(super) mod danger {
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::verify::ServerCertVerifier;
|
||||
use super::ClientConfig;
|
||||
|
||||
/// Accessor for dangerous configuration options.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "dangerous_configuration")))]
|
||||
pub struct DangerousClientConfig<'a> {
|
||||
/// The underlying ClientConfig
|
||||
pub cfg: &'a mut ClientConfig,
|
||||
}
|
||||
|
||||
impl<'a> DangerousClientConfig<'a> {
|
||||
/// Overrides the default `ServerCertVerifier` with something else.
|
||||
pub fn set_certificate_verifier(&mut self, verifier: Arc<dyn ServerCertVerifier>) {
|
||||
self.cfg.verifier = verifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum EarlyDataState {
|
||||
Disabled,
|
||||
Ready,
|
||||
Accepted,
|
||||
AcceptedFinished,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
pub(super) struct EarlyData {
|
||||
state: EarlyDataState,
|
||||
left: usize,
|
||||
}
|
||||
|
||||
impl EarlyData {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
left: 0,
|
||||
state: EarlyDataState::Disabled,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_enabled(&self) -> bool {
|
||||
matches!(self.state, EarlyDataState::Ready | EarlyDataState::Accepted)
|
||||
}
|
||||
|
||||
fn is_accepted(&self) -> bool {
|
||||
matches!(
|
||||
self.state,
|
||||
EarlyDataState::Accepted | EarlyDataState::AcceptedFinished
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn enable(&mut self, max_data: usize) {
|
||||
assert_eq!(self.state, EarlyDataState::Disabled);
|
||||
self.state = EarlyDataState::Ready;
|
||||
self.left = max_data;
|
||||
}
|
||||
|
||||
pub(super) fn rejected(&mut self) {
|
||||
trace!("EarlyData rejected");
|
||||
self.state = EarlyDataState::Rejected;
|
||||
}
|
||||
|
||||
pub(super) fn accepted(&mut self) {
|
||||
trace!("EarlyData accepted");
|
||||
assert_eq!(self.state, EarlyDataState::Ready);
|
||||
self.state = EarlyDataState::Accepted;
|
||||
}
|
||||
|
||||
pub(super) fn finished(&mut self) {
|
||||
trace!("EarlyData finished");
|
||||
self.state = match self.state {
|
||||
EarlyDataState::Accepted => EarlyDataState::AcceptedFinished,
|
||||
_ => panic!("bad EarlyData state"),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_write(&mut self, sz: usize) -> io::Result<usize> {
|
||||
match self.state {
|
||||
EarlyDataState::Disabled => unreachable!(),
|
||||
EarlyDataState::Ready | EarlyDataState::Accepted => {
|
||||
let take = if self.left < sz {
|
||||
mem::replace(&mut self.left, 0)
|
||||
} else {
|
||||
self.left -= sz;
|
||||
sz
|
||||
};
|
||||
|
||||
Ok(take)
|
||||
}
|
||||
EarlyDataState::Rejected | EarlyDataState::AcceptedFinished => {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidInput))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bytes_left(&self) -> usize {
|
||||
self.left
|
||||
}
|
||||
}
|
||||
|
||||
/// Stub that implements io::Write and dispatches to `write_early_data`.
|
||||
pub struct WriteEarlyData<'a> {
|
||||
sess: &'a mut ClientConnection,
|
||||
}
|
||||
|
||||
impl<'a> WriteEarlyData<'a> {
|
||||
fn new(sess: &'a mut ClientConnection) -> WriteEarlyData<'a> {
|
||||
WriteEarlyData { sess }
|
||||
}
|
||||
|
||||
/// How many bytes you may send. Writes will become short
|
||||
/// once this reaches zero.
|
||||
pub fn bytes_left(&self) -> usize {
|
||||
self.sess
|
||||
.inner
|
||||
.data
|
||||
.early_data
|
||||
.bytes_left()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> io::Write for WriteEarlyData<'a> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.sess.write_early_data(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents a single TLS client connection.
|
||||
pub struct ClientConnection {
|
||||
inner: ConnectionCommon<ClientConnectionData>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ClientConnection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("ClientConnection")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientConnection {
|
||||
/// Make a new ClientConnection. `config` controls how
|
||||
/// we behave in the TLS protocol, `name` is the
|
||||
/// name of the server we want to talk to.
|
||||
pub fn new(
|
||||
config: Arc<ClientConfig>,
|
||||
name: ServerName,
|
||||
) -> Result<Self, Error> {
|
||||
Self::new_inner(config, name, Vec::new(), Protocol::Tcp)
|
||||
}
|
||||
|
||||
/// Create a new ClientConnection with a generator.
|
||||
pub fn new_with_session_id_generator<F>(
|
||||
config: Arc<ClientConfig>,
|
||||
name: ServerName,
|
||||
session_id_generator: F,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
F: Fn(&[u8]) -> [u8; 32] + Send + Sync + 'static,
|
||||
{
|
||||
Self::new_inner_with_session_id_generators(
|
||||
config,
|
||||
name,
|
||||
Vec::new(),
|
||||
Protocol::Tcp,
|
||||
ClientSessionIdGenerators {
|
||||
tls13: Some(Arc::new(session_id_generator)),
|
||||
tls12: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new ClientConnection with TLS session-id generators.
|
||||
pub fn new_with_session_id_generators(
|
||||
config: Arc<ClientConfig>,
|
||||
name: ServerName,
|
||||
session_id_generators: ClientSessionIdGenerators,
|
||||
) -> Result<Self, Error> {
|
||||
Self::new_inner_with_session_id_generators(
|
||||
config,
|
||||
name,
|
||||
Vec::new(),
|
||||
Protocol::Tcp,
|
||||
session_id_generators,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
config: Arc<ClientConfig>,
|
||||
name: ServerName,
|
||||
extra_exts: Vec<ClientExtension>,
|
||||
proto: Protocol,
|
||||
) -> Result<Self, Error> {
|
||||
Self::new_inner_with_session_id_generators(
|
||||
config,
|
||||
name,
|
||||
extra_exts,
|
||||
proto,
|
||||
ClientSessionIdGenerators::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn new_inner_with_session_id_generators(
|
||||
config: Arc<ClientConfig>,
|
||||
name: ServerName,
|
||||
extra_exts: Vec<ClientExtension>,
|
||||
proto: Protocol,
|
||||
session_id_generators: ClientSessionIdGenerators,
|
||||
) -> Result<Self, Error> {
|
||||
let mut common_state = CommonState::new(Side::Client);
|
||||
common_state.set_max_fragment_size(config.max_fragment_size)?;
|
||||
common_state.protocol = proto;
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
{
|
||||
common_state.enable_secret_extraction = config.enable_secret_extraction;
|
||||
}
|
||||
let mut data = ClientConnectionData::new();
|
||||
|
||||
let mut cx = hs::ClientContext {
|
||||
common: &mut common_state,
|
||||
data: &mut data,
|
||||
};
|
||||
|
||||
let state =
|
||||
hs::start_handshake(name, extra_exts, config, &mut cx, session_id_generators)?;
|
||||
let inner = ConnectionCommon::new(state, data, common_state);
|
||||
|
||||
Ok(Self { inner })
|
||||
}
|
||||
|
||||
/// Returns an `io::Write` implementer you can write bytes to
|
||||
/// to send TLS1.3 early data (a.k.a. "0-RTT data") to the server.
|
||||
///
|
||||
/// This returns None in many circumstances when the capability to
|
||||
/// send early data is not available, including but not limited to:
|
||||
///
|
||||
/// - The server hasn't been talked to previously.
|
||||
/// - The server does not support resumption.
|
||||
/// - The server does not support early data.
|
||||
/// - The resumption data for the server has expired.
|
||||
///
|
||||
/// The server specifies a maximum amount of early data. You can
|
||||
/// learn this limit through the returned object, and writes through
|
||||
/// it will process only this many bytes.
|
||||
///
|
||||
/// The server can choose not to accept any sent early data --
|
||||
/// in this case the data is lost but the connection continues. You
|
||||
/// can tell this happened using `is_early_data_accepted`.
|
||||
pub fn early_data(&mut self) -> Option<WriteEarlyData> {
|
||||
if self.inner.data.early_data.is_enabled() {
|
||||
Some(WriteEarlyData::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns True if the server signalled it will process early data.
|
||||
///
|
||||
/// If you sent early data and this returns false at the end of the
|
||||
/// handshake then the server will not process the data. This
|
||||
/// is not an error, but you may wish to resend the data.
|
||||
pub fn is_early_data_accepted(&self) -> bool {
|
||||
self.inner.data.early_data.is_accepted()
|
||||
}
|
||||
|
||||
fn write_early_data(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
self.inner
|
||||
.data
|
||||
.early_data
|
||||
.check_write(data.len())
|
||||
.map(|sz| {
|
||||
self.inner
|
||||
.common_state
|
||||
.send_early_plaintext(&data[..sz])
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract secrets, so they can be used when configuring kTLS, for example.
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub fn extract_secrets(self) -> Result<ExtractedSecrets, Error> {
|
||||
self.inner.extract_secrets()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ClientConnection {
|
||||
type Target = ConnectionCommon<ClientConnectionData>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ClientConnection {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a> TryFrom<&'a mut crate::Connection> for &'a mut ClientConnection {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &'a mut crate::Connection) -> Result<Self, Self::Error> {
|
||||
use crate::Connection::*;
|
||||
match value {
|
||||
Client(conn) => Ok(conn),
|
||||
Server(_) => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClientConnection> for crate::Connection {
|
||||
fn from(conn: ClientConnection) -> Self {
|
||||
Self::Client(conn)
|
||||
}
|
||||
}
|
||||
|
||||
/// State associated with a client connection.
|
||||
pub struct ClientConnectionData {
|
||||
pub(super) early_data: EarlyData,
|
||||
pub(super) resumption_ciphersuite: Option<SupportedCipherSuite>,
|
||||
pub(in crate::client) tls12_eager_key_exchanges: Vec<kx::KeyExchange>,
|
||||
}
|
||||
|
||||
impl ClientConnectionData {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
early_data: EarlyData::new(),
|
||||
resumption_ciphersuite: None,
|
||||
tls12_eager_key_exchanges: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::conn::SideData for ClientConnectionData {}
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
impl quic::QuicExt for ClientConnection {
|
||||
fn quic_transport_parameters(&self) -> Option<&[u8]> {
|
||||
self.inner
|
||||
.common_state
|
||||
.quic
|
||||
.params
|
||||
.as_ref()
|
||||
.map(|v| v.as_ref())
|
||||
}
|
||||
|
||||
fn zero_rtt_keys(&self) -> Option<quic::DirectionalKeys> {
|
||||
Some(quic::DirectionalKeys::new(
|
||||
self.inner
|
||||
.data
|
||||
.resumption_ciphersuite
|
||||
.and_then(|suite| suite.tls13())?,
|
||||
self.inner
|
||||
.common_state
|
||||
.quic
|
||||
.early_secret
|
||||
.as_ref()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn read_hs(&mut self, plaintext: &[u8]) -> Result<(), Error> {
|
||||
self.inner.read_quic_hs(plaintext)
|
||||
}
|
||||
|
||||
fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<quic::KeyChange> {
|
||||
quic::write_hs(&mut self.inner.common_state, buf)
|
||||
}
|
||||
|
||||
fn alert(&self) -> Option<AlertDescription> {
|
||||
self.inner.common_state.quic.alert
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods specific to QUIC client sessions
|
||||
#[cfg(feature = "quic")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "quic")))]
|
||||
pub trait ClientQuicExt {
|
||||
/// Make a new QUIC ClientConnection. This differs from `ClientConnection::new()`
|
||||
/// in that it takes an extra argument, `params`, which contains the
|
||||
/// TLS-encoded transport parameters to send.
|
||||
fn new_quic(
|
||||
config: Arc<ClientConfig>,
|
||||
quic_version: quic::Version,
|
||||
name: ServerName,
|
||||
params: Vec<u8>,
|
||||
) -> Result<ClientConnection, Error> {
|
||||
if !config.supports_version(ProtocolVersion::TLSv1_3) {
|
||||
return Err(Error::General(
|
||||
"TLS 1.3 support is required for QUIC".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let ext = match quic_version {
|
||||
quic::Version::V1Draft => ClientExtension::TransportParametersDraft(params),
|
||||
quic::Version::V1 => ClientExtension::TransportParameters(params),
|
||||
};
|
||||
|
||||
// hack: only a work around, do not use it.
|
||||
ClientConnection::new_inner(config, name, vec![ext], Protocol::Quic)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
impl ClientQuicExt for ClientConnection {}
|
||||
114
third_party/rustls-fork-shadow-tls/src/client/common.rs
vendored
Normal file
114
third_party/rustls-fork-shadow-tls/src/client/common.rs
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
use super::ResolvesClientCert;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::{debug, trace};
|
||||
use crate::msgs::enums::ExtensionType;
|
||||
use crate::msgs::handshake::CertificatePayload;
|
||||
use crate::msgs::handshake::SCTList;
|
||||
use crate::msgs::handshake::ServerExtension;
|
||||
use crate::{sign, DistinguishedNames, SignatureScheme};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ServerCertDetails {
|
||||
pub(super) cert_chain: CertificatePayload,
|
||||
pub(super) ocsp_response: Vec<u8>,
|
||||
pub(super) scts: Option<SCTList>,
|
||||
}
|
||||
|
||||
impl ServerCertDetails {
|
||||
pub(super) fn new(
|
||||
cert_chain: CertificatePayload,
|
||||
ocsp_response: Vec<u8>,
|
||||
scts: Option<SCTList>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cert_chain,
|
||||
ocsp_response,
|
||||
scts,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn scts(&self) -> impl Iterator<Item = &[u8]> {
|
||||
self.scts
|
||||
.as_deref()
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.map(|payload| payload.0.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct ClientHelloDetails {
|
||||
pub(super) sent_extensions: Vec<ExtensionType>,
|
||||
}
|
||||
|
||||
impl ClientHelloDetails {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
sent_extensions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn server_may_send_sct_list(&self) -> bool {
|
||||
self.sent_extensions
|
||||
.contains(&ExtensionType::SCT)
|
||||
}
|
||||
|
||||
pub(super) fn server_sent_unsolicited_extensions(
|
||||
&self,
|
||||
received_exts: &[ServerExtension],
|
||||
allowed_unsolicited: &[ExtensionType],
|
||||
) -> bool {
|
||||
for ext in received_exts {
|
||||
let ext_type = ext.get_type();
|
||||
if !self.sent_extensions.contains(&ext_type) && !allowed_unsolicited.contains(&ext_type)
|
||||
{
|
||||
trace!("Unsolicited extension {:?}", ext_type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum ClientAuthDetails {
|
||||
/// Send an empty `Certificate` and no `CertificateVerify`.
|
||||
Empty { auth_context_tls13: Option<Vec<u8>> },
|
||||
/// Send a non-empty `Certificate` and a `CertificateVerify`.
|
||||
Verify {
|
||||
certkey: Arc<sign::CertifiedKey>,
|
||||
signer: Box<dyn sign::Signer>,
|
||||
auth_context_tls13: Option<Vec<u8>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ClientAuthDetails {
|
||||
pub(super) fn resolve(
|
||||
resolver: &dyn ResolvesClientCert,
|
||||
canames: Option<&DistinguishedNames>,
|
||||
sigschemes: &[SignatureScheme],
|
||||
auth_context_tls13: Option<Vec<u8>>,
|
||||
) -> Self {
|
||||
let acceptable_issuers = canames
|
||||
.map(Vec::as_slice)
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|p| p.0.as_slice())
|
||||
.collect::<Vec<&[u8]>>();
|
||||
|
||||
if let Some(certkey) = resolver.resolve(&acceptable_issuers, sigschemes) {
|
||||
if let Some(signer) = certkey.key.choose_scheme(sigschemes) {
|
||||
debug!("Attempting client auth");
|
||||
return Self::Verify {
|
||||
certkey,
|
||||
signer,
|
||||
auth_context_tls13,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Client auth requested but no cert/sigscheme available");
|
||||
Self::Empty { auth_context_tls13 }
|
||||
}
|
||||
}
|
||||
161
third_party/rustls-fork-shadow-tls/src/client/handy.rs
vendored
Normal file
161
third_party/rustls-fork-shadow-tls/src/client/handy.rs
vendored
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
use crate::client;
|
||||
use crate::enums::SignatureScheme;
|
||||
use crate::error::Error;
|
||||
use crate::key;
|
||||
use crate::limited_cache;
|
||||
use crate::sign;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// An implementer of `StoresClientSessions` which does nothing.
|
||||
pub struct NoClientSessionStorage {}
|
||||
|
||||
impl client::StoresClientSessions for NoClientSessionStorage {
|
||||
fn put(&self, _key: Vec<u8>, _value: Vec<u8>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get(&self, _key: &[u8]) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementer of `StoresClientSessions` that stores everything
|
||||
/// in memory. It enforces a limit on the number of entries
|
||||
/// to bound memory usage.
|
||||
pub struct ClientSessionMemoryCache {
|
||||
cache: Mutex<limited_cache::LimitedCache<Vec<u8>, Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl ClientSessionMemoryCache {
|
||||
/// Make a new ClientSessionMemoryCache. `size` is the
|
||||
/// maximum number of stored sessions.
|
||||
pub fn new(size: usize) -> Arc<Self> {
|
||||
debug_assert!(size > 0);
|
||||
Arc::new(Self {
|
||||
cache: Mutex::new(limited_cache::LimitedCache::new(size)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl client::StoresClientSessions for ClientSessionMemoryCache {
|
||||
fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool {
|
||||
self.cache
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(key, value);
|
||||
true
|
||||
}
|
||||
|
||||
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.cache
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(key)
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct FailResolveClientCert {}
|
||||
|
||||
impl client::ResolvesClientCert for FailResolveClientCert {
|
||||
fn resolve(
|
||||
&self,
|
||||
_acceptable_issuers: &[&[u8]],
|
||||
_sigschemes: &[SignatureScheme],
|
||||
) -> Option<Arc<sign::CertifiedKey>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn has_certs(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct AlwaysResolvesClientCert(Arc<sign::CertifiedKey>);
|
||||
|
||||
impl AlwaysResolvesClientCert {
|
||||
pub(super) fn new(
|
||||
chain: Vec<key::Certificate>,
|
||||
priv_key: &key::PrivateKey,
|
||||
) -> Result<Self, Error> {
|
||||
let key = sign::any_supported_type(priv_key)
|
||||
.map_err(|_| Error::General("invalid private key".into()))?;
|
||||
Ok(Self(Arc::new(sign::CertifiedKey::new(chain, key))))
|
||||
}
|
||||
}
|
||||
|
||||
impl client::ResolvesClientCert for AlwaysResolvesClientCert {
|
||||
fn resolve(
|
||||
&self,
|
||||
_acceptable_issuers: &[&[u8]],
|
||||
_sigschemes: &[SignatureScheme],
|
||||
) -> Option<Arc<sign::CertifiedKey>> {
|
||||
Some(Arc::clone(&self.0))
|
||||
}
|
||||
|
||||
fn has_certs(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::client::StoresClientSessions;
|
||||
|
||||
#[test]
|
||||
fn test_noclientsessionstorage_drops_put() {
|
||||
let c = NoClientSessionStorage {};
|
||||
assert!(!c.put(vec![0x01], vec![0x02]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_noclientsessionstorage_denies_gets() {
|
||||
let c = NoClientSessionStorage {};
|
||||
c.put(vec![0x01], vec![0x02]);
|
||||
assert_eq!(c.get(&[]), None);
|
||||
assert_eq!(c.get(&[0x01]), None);
|
||||
assert_eq!(c.get(&[0x02]), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clientsessionmemorycache_accepts_put() {
|
||||
let c = ClientSessionMemoryCache::new(4);
|
||||
assert!(c.put(vec![0x01], vec![0x02]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clientsessionmemorycache_persists_put() {
|
||||
let c = ClientSessionMemoryCache::new(4);
|
||||
assert!(c.put(vec![0x01], vec![0x02]));
|
||||
assert_eq!(c.get(&[0x01]), Some(vec![0x02]));
|
||||
assert_eq!(c.get(&[0x01]), Some(vec![0x02]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clientsessionmemorycache_overwrites_put() {
|
||||
let c = ClientSessionMemoryCache::new(4);
|
||||
assert!(c.put(vec![0x01], vec![0x02]));
|
||||
assert!(c.put(vec![0x01], vec![0x04]));
|
||||
assert_eq!(c.get(&[0x01]), Some(vec![0x04]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clientsessionmemorycache_drops_to_maintain_size_invariant() {
|
||||
let c = ClientSessionMemoryCache::new(2);
|
||||
assert!(c.put(vec![0x01], vec![0x02]));
|
||||
assert!(c.put(vec![0x03], vec![0x04]));
|
||||
assert!(c.put(vec![0x05], vec![0x06]));
|
||||
assert!(c.put(vec![0x07], vec![0x08]));
|
||||
assert!(c.put(vec![0x09], vec![0x0a]));
|
||||
|
||||
let count = c.get(&[0x01]).iter().count()
|
||||
+ c.get(&[0x03]).iter().count()
|
||||
+ c.get(&[0x05]).iter().count()
|
||||
+ c.get(&[0x07]).iter().count()
|
||||
+ c.get(&[0x09]).iter().count();
|
||||
|
||||
assert!(count < 5);
|
||||
}
|
||||
}
|
||||
891
third_party/rustls-fork-shadow-tls/src/client/hs.rs
vendored
Normal file
891
third_party/rustls-fork-shadow-tls/src/client/hs.rs
vendored
Normal file
|
|
@ -0,0 +1,891 @@
|
|||
#[cfg(feature = "logging")]
|
||||
use crate::bs_debug;
|
||||
use crate::check::inappropriate_handshake_message;
|
||||
use crate::conn::{CommonState, ConnectionRandoms, State};
|
||||
use crate::enums::{CipherSuite, ProtocolVersion};
|
||||
use crate::error::Error;
|
||||
use crate::hash_hs::HandshakeHashBuffer;
|
||||
use crate::kx;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::{debug, trace};
|
||||
use crate::msgs::base::Payload;
|
||||
#[cfg(feature = "quic")]
|
||||
use crate::msgs::base::PayloadU16;
|
||||
use crate::msgs::codec::{Codec, Reader};
|
||||
use crate::msgs::enums::{AlertDescription, Compression, ContentType};
|
||||
use crate::msgs::enums::{ECPointFormat, PSKKeyExchangeMode};
|
||||
use crate::msgs::enums::{ExtensionType, HandshakeType};
|
||||
use crate::msgs::handshake::{CertificateStatusRequest, ClientSessionTicket, SCTList};
|
||||
use crate::msgs::handshake::{ClientExtension, HasServerExtensions};
|
||||
use crate::msgs::handshake::{ClientHelloPayload, HandshakeMessagePayload, HandshakePayload};
|
||||
use crate::msgs::handshake::{ConvertProtocolNameList, ProtocolNameList};
|
||||
use crate::msgs::handshake::{ECPointFormatList, SupportedPointFormats};
|
||||
use crate::msgs::handshake::{HelloRetryRequest, KeyShareEntry};
|
||||
use crate::msgs::handshake::{Random, SessionID};
|
||||
use crate::msgs::message::{Message, MessagePayload};
|
||||
use crate::msgs::persist;
|
||||
use crate::ticketer::TimeBase;
|
||||
use crate::tls13::key_schedule::KeyScheduleEarly;
|
||||
use crate::SupportedCipherSuite;
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
use super::tls12;
|
||||
use crate::client::client_conn::{ClientConnectionData, ClientSessionIdGenerators};
|
||||
use crate::client::common::ClientHelloDetails;
|
||||
use crate::client::{tls13, ClientConfig, ServerName};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(super) type NextState = Box<dyn State<ClientConnectionData>>;
|
||||
pub(super) type NextStateOrError = Result<NextState, Error>;
|
||||
pub(super) type ClientContext<'a> = crate::conn::Context<'a, ClientConnectionData>;
|
||||
|
||||
fn find_session(
|
||||
server_name: &ServerName,
|
||||
config: &ClientConfig,
|
||||
#[cfg(feature = "quic")] cx: &mut ClientContext<'_>,
|
||||
) -> Option<persist::Retrieved<persist::ClientSessionValue>> {
|
||||
let key = persist::ClientSessionKey::session_for_server_name(server_name);
|
||||
let key_buf = key.get_encoding();
|
||||
|
||||
let value = config
|
||||
.session_storage
|
||||
.get(&key_buf)
|
||||
.or_else(|| {
|
||||
debug!("No cached session for {:?}", server_name);
|
||||
None
|
||||
})?;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut reader = Reader::init(&value[2..]);
|
||||
#[allow(clippy::bind_instead_of_map)] // https://github.com/rust-lang/rust-clippy/issues/8082
|
||||
CipherSuite::read_bytes(&value[..2])
|
||||
.and_then(|suite| {
|
||||
persist::ClientSessionValue::read(&mut reader, suite, &config.cipher_suites)
|
||||
})
|
||||
.and_then(|resuming| {
|
||||
let retrieved = persist::Retrieved::new(resuming, TimeBase::now().ok()?);
|
||||
match retrieved.has_expired() {
|
||||
false => Some(retrieved),
|
||||
true => None,
|
||||
}
|
||||
})
|
||||
.and_then(|resuming| {
|
||||
#[cfg(feature = "quic")]
|
||||
if cx.common.is_quic() {
|
||||
let params = PayloadU16::read(&mut reader)?;
|
||||
cx.common.quic.params = Some(params.0);
|
||||
}
|
||||
Some(resuming)
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn start_handshake(
|
||||
server_name: ServerName,
|
||||
extra_exts: Vec<ClientExtension>,
|
||||
config: Arc<ClientConfig>,
|
||||
cx: &mut ClientContext<'_>,
|
||||
session_id_generators: ClientSessionIdGenerators,
|
||||
) -> NextStateOrError {
|
||||
let mut transcript_buffer = HandshakeHashBuffer::new();
|
||||
if config
|
||||
.client_auth_cert_resolver
|
||||
.has_certs()
|
||||
{
|
||||
transcript_buffer.set_client_auth_enabled();
|
||||
}
|
||||
|
||||
let support_tls13 = config.supports_version(ProtocolVersion::TLSv1_3);
|
||||
|
||||
let mut session_id: Option<SessionID> = None;
|
||||
let mut resuming_session = find_session(
|
||||
&server_name,
|
||||
&config,
|
||||
#[cfg(feature = "quic")]
|
||||
cx,
|
||||
);
|
||||
|
||||
let key_share = if support_tls13 {
|
||||
Some(tls13::initial_key_share(&config, &server_name)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(_resuming) = &mut resuming_session {
|
||||
#[cfg(feature = "tls12")]
|
||||
if let persist::ClientSessionValue::Tls12(inner) = &mut _resuming.value {
|
||||
// If we have a ticket, we use the sessionid as a signal that
|
||||
// we're doing an abbreviated handshake. See section 3.4 in
|
||||
// RFC5077.
|
||||
if !inner.ticket().is_empty() {
|
||||
inner.session_id = SessionID::random()?;
|
||||
}
|
||||
session_id = Some(inner.session_id);
|
||||
}
|
||||
|
||||
debug!("Resuming session");
|
||||
} else {
|
||||
debug!("Not resuming any session");
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc8446#appendix-D.4
|
||||
// https://tools.ietf.org/html/draft-ietf-quic-tls-34#section-8.4
|
||||
if session_id.is_none() && !cx.common.is_quic() {
|
||||
session_id = Some(SessionID::random()?);
|
||||
}
|
||||
|
||||
let random = Random::new()?;
|
||||
let hello_details = ClientHelloDetails::new();
|
||||
let sent_tls13_fake_ccs = false;
|
||||
let may_send_sct_list = config.verifier.request_scts();
|
||||
Ok(emit_client_hello_for_retry(
|
||||
config,
|
||||
cx,
|
||||
resuming_session,
|
||||
random,
|
||||
false,
|
||||
transcript_buffer,
|
||||
sent_tls13_fake_ccs,
|
||||
hello_details,
|
||||
session_id,
|
||||
session_id_generators,
|
||||
None,
|
||||
server_name,
|
||||
key_share,
|
||||
extra_exts,
|
||||
may_send_sct_list,
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
struct ExpectServerHello {
|
||||
config: Arc<ClientConfig>,
|
||||
resuming_session: Option<persist::Retrieved<persist::ClientSessionValue>>,
|
||||
server_name: ServerName,
|
||||
random: Random,
|
||||
using_ems: bool,
|
||||
transcript_buffer: HandshakeHashBuffer,
|
||||
early_key_schedule: Option<KeyScheduleEarly>,
|
||||
hello: ClientHelloDetails,
|
||||
offered_key_share: Option<kx::KeyExchange>,
|
||||
session_id: SessionID,
|
||||
sent_tls13_fake_ccs: bool,
|
||||
suite: Option<SupportedCipherSuite>,
|
||||
}
|
||||
|
||||
struct ExpectServerHelloOrHelloRetryRequest {
|
||||
next: ExpectServerHello,
|
||||
extra_exts: Vec<ClientExtension>,
|
||||
}
|
||||
|
||||
fn emit_client_hello_for_retry(
|
||||
config: Arc<ClientConfig>,
|
||||
cx: &mut ClientContext<'_>,
|
||||
resuming_session: Option<persist::Retrieved<persist::ClientSessionValue>>,
|
||||
random: Random,
|
||||
using_ems: bool,
|
||||
mut transcript_buffer: HandshakeHashBuffer,
|
||||
mut sent_tls13_fake_ccs: bool,
|
||||
mut hello: ClientHelloDetails,
|
||||
session_id: Option<SessionID>,
|
||||
session_id_generators: ClientSessionIdGenerators,
|
||||
retryreq: Option<&HelloRetryRequest>,
|
||||
server_name: ServerName,
|
||||
key_share: Option<kx::KeyExchange>,
|
||||
extra_exts: Vec<ClientExtension>,
|
||||
may_send_sct_list: bool,
|
||||
suite: Option<SupportedCipherSuite>,
|
||||
) -> NextState {
|
||||
// Do we have a SessionID or ticket cached for this host?
|
||||
let (ticket, resume_version) = if let Some(resuming) = &resuming_session {
|
||||
match &resuming.value {
|
||||
persist::ClientSessionValue::Tls13(inner) => {
|
||||
(inner.ticket().to_vec(), ProtocolVersion::TLSv1_3)
|
||||
}
|
||||
#[cfg(feature = "tls12")]
|
||||
persist::ClientSessionValue::Tls12(inner) => {
|
||||
(inner.ticket().to_vec(), ProtocolVersion::TLSv1_2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(Vec::new(), ProtocolVersion::Unknown(0))
|
||||
};
|
||||
|
||||
let support_tls12 = config.supports_version(ProtocolVersion::TLSv1_2) && !cx.common.is_quic();
|
||||
let support_tls13 = config.supports_version(ProtocolVersion::TLSv1_3);
|
||||
|
||||
let mut supported_versions = Vec::new();
|
||||
if support_tls13 {
|
||||
supported_versions.push(ProtocolVersion::TLSv1_3);
|
||||
}
|
||||
|
||||
if support_tls12 {
|
||||
supported_versions.push(ProtocolVersion::TLSv1_2);
|
||||
}
|
||||
|
||||
cx.data.tls12_eager_key_exchanges.clear();
|
||||
let tls12_session_id_materials = if retryreq.is_none()
|
||||
&& support_tls12
|
||||
&& session_id_generators.tls12.is_some()
|
||||
{
|
||||
let mut materials = Vec::new();
|
||||
for &group in &config.kx_groups {
|
||||
if let Some(kx) = kx::KeyExchange::start(group) {
|
||||
materials.push(kx.pubkey.as_ref().to_vec());
|
||||
cx.data.tls12_eager_key_exchanges.push(kx);
|
||||
}
|
||||
}
|
||||
if resume_version == ProtocolVersion::TLSv1_2 && !ticket.is_empty() {
|
||||
materials.push(ticket.clone());
|
||||
}
|
||||
Some(materials)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// should be unreachable thanks to config builder
|
||||
assert!(!supported_versions.is_empty());
|
||||
|
||||
let mut exts = vec![
|
||||
ClientExtension::SupportedVersions(supported_versions),
|
||||
ClientExtension::ECPointFormats(ECPointFormatList::supported()),
|
||||
ClientExtension::NamedGroups(
|
||||
config
|
||||
.kx_groups
|
||||
.iter()
|
||||
.map(|skxg| skxg.name)
|
||||
.collect(),
|
||||
),
|
||||
ClientExtension::SignatureAlgorithms(
|
||||
config
|
||||
.verifier
|
||||
.supported_verify_schemes(),
|
||||
),
|
||||
ClientExtension::ExtendedMasterSecretRequest,
|
||||
ClientExtension::CertificateStatusRequest(CertificateStatusRequest::build_ocsp()),
|
||||
];
|
||||
|
||||
if let (Some(sni_name), true) = (server_name.for_sni(), config.enable_sni) {
|
||||
exts.push(ClientExtension::make_sni(sni_name));
|
||||
}
|
||||
|
||||
if may_send_sct_list {
|
||||
exts.push(ClientExtension::SignedCertificateTimestampRequest);
|
||||
}
|
||||
|
||||
if let Some(key_share) = &key_share {
|
||||
debug_assert!(support_tls13);
|
||||
let key_share = KeyShareEntry::new(key_share.group(), key_share.pubkey.as_ref());
|
||||
exts.push(ClientExtension::KeyShare(vec![key_share]));
|
||||
}
|
||||
|
||||
if let Some(cookie) = retryreq.and_then(HelloRetryRequest::get_cookie) {
|
||||
exts.push(ClientExtension::Cookie(cookie.clone()));
|
||||
}
|
||||
|
||||
if support_tls13 && config.enable_tickets {
|
||||
// We could support PSK_KE here too. Such connections don't
|
||||
// have forward secrecy, and are similar to TLS1.2 resumption.
|
||||
let psk_modes = vec![PSKKeyExchangeMode::PSK_DHE_KE];
|
||||
exts.push(ClientExtension::PresharedKeyModes(psk_modes));
|
||||
}
|
||||
|
||||
if !config.alpn_protocols.is_empty() {
|
||||
exts.push(ClientExtension::Protocols(ProtocolNameList::from_slices(
|
||||
&config
|
||||
.alpn_protocols
|
||||
.iter()
|
||||
.map(|proto| &proto[..])
|
||||
.collect::<Vec<_>>(),
|
||||
)));
|
||||
}
|
||||
|
||||
// Extra extensions must be placed before the PSK extension
|
||||
exts.extend(extra_exts.iter().cloned());
|
||||
|
||||
let fill_in_binder = if support_tls13
|
||||
&& config.enable_tickets
|
||||
&& resume_version == ProtocolVersion::TLSv1_3
|
||||
&& !ticket.is_empty()
|
||||
{
|
||||
resuming_session
|
||||
.as_ref()
|
||||
.and_then(|resuming| match (suite, resuming.tls13()) {
|
||||
(Some(suite), Some(resuming)) => {
|
||||
suite
|
||||
.tls13()?
|
||||
.can_resume_from(resuming.suite())?;
|
||||
Some(resuming)
|
||||
}
|
||||
(None, Some(resuming)) => Some(resuming),
|
||||
_ => None,
|
||||
})
|
||||
.map(|resuming| {
|
||||
tls13::prepare_resumption(
|
||||
&config,
|
||||
cx,
|
||||
ticket,
|
||||
&resuming,
|
||||
&mut exts,
|
||||
retryreq.is_some(),
|
||||
);
|
||||
resuming
|
||||
})
|
||||
} else if config.enable_tickets {
|
||||
// If we have a ticket, include it. Otherwise, request one.
|
||||
if ticket.is_empty() {
|
||||
exts.push(ClientExtension::SessionTicket(ClientSessionTicket::Request));
|
||||
} else {
|
||||
exts.push(ClientExtension::SessionTicket(ClientSessionTicket::Offer(
|
||||
Payload::new(ticket),
|
||||
)));
|
||||
}
|
||||
None
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Note what extensions we sent.
|
||||
hello.sent_extensions = exts
|
||||
.iter()
|
||||
.map(ClientExtension::get_type)
|
||||
.collect();
|
||||
|
||||
let mut session_id = session_id.unwrap_or_else(SessionID::empty);
|
||||
let mut cipher_suites: Vec<_> = config
|
||||
.cipher_suites
|
||||
.iter()
|
||||
.map(|cs| cs.suite())
|
||||
.collect();
|
||||
// We don't do renegotiation at all, in fact.
|
||||
cipher_suites.push(CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
|
||||
|
||||
let mut chp = HandshakeMessagePayload {
|
||||
typ: HandshakeType::ClientHello,
|
||||
payload: HandshakePayload::ClientHello(ClientHelloPayload {
|
||||
client_version: ProtocolVersion::TLSv1_2,
|
||||
random,
|
||||
session_id,
|
||||
cipher_suites,
|
||||
compression_methods: vec![Compression::Null],
|
||||
extensions: exts,
|
||||
}),
|
||||
};
|
||||
|
||||
// hack: sign chp and overwrite session id
|
||||
if session_id_generators.tls13.is_some() || tls12_session_id_materials.is_some() {
|
||||
let mut buffer = Vec::new();
|
||||
match &mut chp.payload {
|
||||
HandshakePayload::ClientHello(c) => {
|
||||
c.session_id = SessionID::zero();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
chp.encode(&mut buffer);
|
||||
let generated_session_id = if let (Some(generator), Some(materials)) = (
|
||||
session_id_generators.tls12.as_ref(),
|
||||
tls12_session_id_materials.as_ref(),
|
||||
) {
|
||||
generator(&buffer, materials)
|
||||
} else if let Some(generator) = session_id_generators.tls13.as_ref() {
|
||||
generator(&buffer)
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
session_id = SessionID {
|
||||
len: 32,
|
||||
data: generated_session_id,
|
||||
};
|
||||
match &mut chp.payload {
|
||||
HandshakePayload::ClientHello(c) => {
|
||||
c.session_id = session_id;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let early_key_schedule = if let Some(resuming) = fill_in_binder {
|
||||
let schedule = tls13::fill_in_psk_binder(&resuming, &transcript_buffer, &mut chp);
|
||||
Some((resuming.suite(), schedule))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ch = Message {
|
||||
// "This value MUST be set to 0x0303 for all records generated
|
||||
// by a TLS 1.3 implementation other than an initial ClientHello
|
||||
// (i.e., one not generated after a HelloRetryRequest)"
|
||||
version: if retryreq.is_some() {
|
||||
ProtocolVersion::TLSv1_2
|
||||
} else {
|
||||
ProtocolVersion::TLSv1_0
|
||||
},
|
||||
payload: MessagePayload::handshake(chp),
|
||||
};
|
||||
|
||||
if retryreq.is_some() {
|
||||
// send dummy CCS to fool middleboxes prior
|
||||
// to second client hello
|
||||
tls13::emit_fake_ccs(&mut sent_tls13_fake_ccs, cx.common);
|
||||
}
|
||||
|
||||
trace!("Sending ClientHello {:#?}", ch);
|
||||
|
||||
transcript_buffer.add_message(&ch);
|
||||
cx.common.send_msg(ch, false);
|
||||
|
||||
// Calculate the hash of ClientHello and use it to derive EarlyTrafficSecret
|
||||
let early_key_schedule = early_key_schedule.map(|(resuming_suite, schedule)| {
|
||||
if !cx.data.early_data.is_enabled() {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
tls13::derive_early_traffic_secret(
|
||||
&*config.key_log,
|
||||
cx,
|
||||
resuming_suite,
|
||||
&schedule,
|
||||
&mut sent_tls13_fake_ccs,
|
||||
&transcript_buffer,
|
||||
&random.0,
|
||||
);
|
||||
schedule
|
||||
});
|
||||
|
||||
let next = ExpectServerHello {
|
||||
config,
|
||||
resuming_session,
|
||||
server_name,
|
||||
random,
|
||||
using_ems,
|
||||
transcript_buffer,
|
||||
early_key_schedule,
|
||||
hello,
|
||||
offered_key_share: key_share,
|
||||
session_id,
|
||||
sent_tls13_fake_ccs,
|
||||
suite,
|
||||
};
|
||||
|
||||
if support_tls13 && retryreq.is_none() {
|
||||
Box::new(ExpectServerHelloOrHelloRetryRequest { next, extra_exts })
|
||||
} else {
|
||||
Box::new(next)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn process_alpn_protocol(
|
||||
common: &mut CommonState,
|
||||
config: &ClientConfig,
|
||||
proto: Option<&[u8]>,
|
||||
) -> Result<(), Error> {
|
||||
common.alpn_protocol = proto.map(ToOwned::to_owned);
|
||||
|
||||
if let Some(alpn_protocol) = &common.alpn_protocol {
|
||||
if !config
|
||||
.alpn_protocols
|
||||
.contains(alpn_protocol)
|
||||
{
|
||||
return Err(common.illegal_param("server sent non-offered ALPN protocol"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
{
|
||||
// RFC 9001 says: "While ALPN only specifies that servers use this alert, QUIC clients MUST
|
||||
// use error 0x0178 to terminate a connection when ALPN negotiation fails." We judge that
|
||||
// the user intended to use ALPN (rather than some out-of-band protocol negotiation
|
||||
// mechanism) iff any ALPN protocols were configured. This defends against badly-behaved
|
||||
// servers which accept a connection that requires an application-layer protocol they do not
|
||||
// understand.
|
||||
if common.is_quic() && common.alpn_protocol.is_none() && !config.alpn_protocols.is_empty() {
|
||||
common.send_fatal_alert(AlertDescription::NoApplicationProtocol);
|
||||
return Err(Error::NoApplicationProtocol);
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"ALPN protocol is {:?}",
|
||||
common
|
||||
.alpn_protocol
|
||||
.as_ref()
|
||||
.map(|v| bs_debug::BsDebug(v))
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn sct_list_is_invalid(scts: &SCTList) -> bool {
|
||||
scts.is_empty() || scts.iter().any(|sct| sct.0.is_empty())
|
||||
}
|
||||
|
||||
impl State<ClientConnectionData> for ExpectServerHello {
|
||||
fn handle(mut self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> NextStateOrError {
|
||||
let server_hello =
|
||||
require_handshake_msg!(m, HandshakeType::ServerHello, HandshakePayload::ServerHello)?;
|
||||
trace!("We got ServerHello {:#?}", server_hello);
|
||||
|
||||
use crate::ProtocolVersion::{TLSv1_2, TLSv1_3};
|
||||
let tls13_supported = self.config.supports_version(TLSv1_3);
|
||||
|
||||
let server_version = if server_hello.legacy_version == TLSv1_2 {
|
||||
server_hello
|
||||
.get_supported_versions()
|
||||
.unwrap_or(server_hello.legacy_version)
|
||||
} else {
|
||||
server_hello.legacy_version
|
||||
};
|
||||
|
||||
let version = match server_version {
|
||||
TLSv1_3 if tls13_supported => TLSv1_3,
|
||||
TLSv1_2 if self.config.supports_version(TLSv1_2) => {
|
||||
if cx.data.early_data.is_enabled() && cx.common.early_traffic {
|
||||
// The client must fail with a dedicated error code if the server
|
||||
// responds with TLS 1.2 when offering 0-RTT.
|
||||
return Err(Error::PeerMisbehavedError(
|
||||
"server chose v1.2 when offering 0-rtt".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if server_hello
|
||||
.get_supported_versions()
|
||||
.is_some()
|
||||
{
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server chose v1.2 using v1.3 extension"));
|
||||
}
|
||||
|
||||
TLSv1_2
|
||||
}
|
||||
_ => {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::ProtocolVersion);
|
||||
let msg = match server_version {
|
||||
TLSv1_2 | TLSv1_3 => "server's TLS version is disabled in client",
|
||||
_ => "server does not support TLS v1.2/v1.3",
|
||||
};
|
||||
return Err(Error::PeerIncompatibleError(msg.to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
if server_hello.compression_method != Compression::Null {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server chose non-Null compression"));
|
||||
}
|
||||
|
||||
if server_hello.has_duplicate_extension() {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(Error::PeerMisbehavedError(
|
||||
"server sent duplicate extensions".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let allowed_unsolicited = [ExtensionType::RenegotiationInfo];
|
||||
if self
|
||||
.hello
|
||||
.server_sent_unsolicited_extensions(&server_hello.extensions, &allowed_unsolicited)
|
||||
{
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
return Err(Error::PeerMisbehavedError(
|
||||
"server sent unsolicited extension".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
cx.common.negotiated_version = Some(version);
|
||||
|
||||
// Extract ALPN protocol
|
||||
if !cx.common.is_tls13() {
|
||||
process_alpn_protocol(cx.common, &self.config, server_hello.get_alpn_protocol())?;
|
||||
}
|
||||
|
||||
// If ECPointFormats extension is supplied by the server, it must contain
|
||||
// Uncompressed. But it's allowed to be omitted.
|
||||
if let Some(point_fmts) = server_hello.get_ecpoints_extension() {
|
||||
if !point_fmts.contains(&ECPointFormat::Uncompressed) {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::HandshakeFailure);
|
||||
return Err(Error::PeerMisbehavedError(
|
||||
"server does not support uncompressed points".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let suite = self
|
||||
.config
|
||||
.find_cipher_suite(server_hello.cipher_suite)
|
||||
.ok_or_else(|| {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::HandshakeFailure);
|
||||
Error::PeerMisbehavedError("server chose non-offered ciphersuite".to_string())
|
||||
})?;
|
||||
|
||||
if version != suite.version().version {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server chose unusable ciphersuite for version"));
|
||||
}
|
||||
|
||||
match self.suite {
|
||||
Some(prev_suite) if prev_suite != suite => {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server varied selected ciphersuite"));
|
||||
}
|
||||
_ => {
|
||||
debug!("Using ciphersuite {:?}", suite);
|
||||
self.suite = Some(suite);
|
||||
cx.common.suite = Some(suite);
|
||||
}
|
||||
}
|
||||
|
||||
// Start our handshake hash, and input the server-hello.
|
||||
let mut transcript = self
|
||||
.transcript_buffer
|
||||
.start_hash(suite.hash_algorithm());
|
||||
transcript.add_message(&m);
|
||||
|
||||
let randoms = ConnectionRandoms::new(self.random, server_hello.random);
|
||||
// For TLS1.3, start message encryption using
|
||||
// handshake_traffic_secret.
|
||||
match suite {
|
||||
SupportedCipherSuite::Tls13(suite) => {
|
||||
let resuming_session = self
|
||||
.resuming_session
|
||||
.and_then(|resuming| match resuming.value {
|
||||
persist::ClientSessionValue::Tls13(inner) => Some(inner),
|
||||
#[cfg(feature = "tls12")]
|
||||
persist::ClientSessionValue::Tls12(_) => None,
|
||||
});
|
||||
|
||||
tls13::handle_server_hello(
|
||||
self.config,
|
||||
cx,
|
||||
server_hello,
|
||||
resuming_session,
|
||||
self.server_name,
|
||||
randoms,
|
||||
suite,
|
||||
transcript,
|
||||
self.early_key_schedule,
|
||||
self.hello,
|
||||
// We always send a key share when TLS 1.3 is enabled.
|
||||
self.offered_key_share.unwrap(),
|
||||
self.sent_tls13_fake_ccs,
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "tls12")]
|
||||
SupportedCipherSuite::Tls12(suite) => {
|
||||
let resuming_session = self
|
||||
.resuming_session
|
||||
.and_then(|resuming| match resuming.value {
|
||||
persist::ClientSessionValue::Tls12(inner) => Some(inner),
|
||||
persist::ClientSessionValue::Tls13(_) => None,
|
||||
});
|
||||
|
||||
tls12::CompleteServerHelloHandling {
|
||||
config: self.config,
|
||||
resuming_session,
|
||||
server_name: self.server_name,
|
||||
randoms,
|
||||
using_ems: self.using_ems,
|
||||
transcript,
|
||||
}
|
||||
.handle_server_hello(cx, suite, server_hello, tls13_supported)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExpectServerHelloOrHelloRetryRequest {
|
||||
fn into_expect_server_hello(self) -> NextState {
|
||||
Box::new(self.next)
|
||||
}
|
||||
|
||||
fn handle_hello_retry_request(
|
||||
self,
|
||||
cx: &mut ClientContext<'_>,
|
||||
m: Message,
|
||||
) -> NextStateOrError {
|
||||
let hrr = require_handshake_msg!(
|
||||
m,
|
||||
HandshakeType::HelloRetryRequest,
|
||||
HandshakePayload::HelloRetryRequest
|
||||
)?;
|
||||
trace!("Got HRR {:?}", hrr);
|
||||
|
||||
cx.common.check_aligned_handshake()?;
|
||||
|
||||
let cookie = hrr.get_cookie();
|
||||
let req_group = hrr.get_requested_key_share_group();
|
||||
|
||||
// We always send a key share when TLS 1.3 is enabled.
|
||||
let offered_key_share = self.next.offered_key_share.unwrap();
|
||||
|
||||
// A retry request is illegal if it contains no cookie and asks for
|
||||
// retry of a group we already sent.
|
||||
if cookie.is_none() && req_group == Some(offered_key_share.group()) {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server requested hrr with our group"));
|
||||
}
|
||||
|
||||
// Or has an empty cookie.
|
||||
if let Some(cookie) = cookie {
|
||||
if cookie.0.is_empty() {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server requested hrr with empty cookie"));
|
||||
}
|
||||
}
|
||||
|
||||
// Or has something unrecognised
|
||||
if hrr.has_unknown_extension() {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
return Err(Error::PeerIncompatibleError(
|
||||
"server sent hrr with unhandled extension".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// Or has the same extensions more than once
|
||||
if hrr.has_duplicate_extension() {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server send duplicate hrr extensions"));
|
||||
}
|
||||
|
||||
// Or asks us to change nothing.
|
||||
if cookie.is_none() && req_group.is_none() {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server requested hrr with no changes"));
|
||||
}
|
||||
|
||||
// Or asks us to talk a protocol we didn't offer, or doesn't support HRR at all.
|
||||
match hrr.get_supported_versions() {
|
||||
Some(ProtocolVersion::TLSv1_3) => {
|
||||
cx.common.negotiated_version = Some(ProtocolVersion::TLSv1_3);
|
||||
}
|
||||
_ => {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server requested unsupported version in hrr"));
|
||||
}
|
||||
}
|
||||
|
||||
// Or asks us to use a ciphersuite we didn't offer.
|
||||
let maybe_cs = self
|
||||
.next
|
||||
.config
|
||||
.find_cipher_suite(hrr.cipher_suite);
|
||||
let cs = match maybe_cs {
|
||||
Some(cs) => cs,
|
||||
None => {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("server requested unsupported cs in hrr"));
|
||||
}
|
||||
};
|
||||
|
||||
// HRR selects the ciphersuite.
|
||||
cx.common.suite = Some(cs);
|
||||
|
||||
// This is the draft19 change where the transcript became a tree
|
||||
let transcript = self
|
||||
.next
|
||||
.transcript_buffer
|
||||
.start_hash(cs.hash_algorithm());
|
||||
let mut transcript_buffer = transcript.into_hrr_buffer();
|
||||
transcript_buffer.add_message(&m);
|
||||
|
||||
// Early data is not allowed after HelloRetryrequest
|
||||
if cx.data.early_data.is_enabled() {
|
||||
cx.data.early_data.rejected();
|
||||
}
|
||||
|
||||
let may_send_sct_list = self
|
||||
.next
|
||||
.hello
|
||||
.server_may_send_sct_list();
|
||||
|
||||
let key_share = match req_group {
|
||||
Some(group) if group != offered_key_share.group() => {
|
||||
let group = kx::KeyExchange::choose(group, &self.next.config.kx_groups)
|
||||
.ok_or_else(|| {
|
||||
cx.common
|
||||
.illegal_param("server requested hrr with bad group")
|
||||
})?;
|
||||
kx::KeyExchange::start(group).ok_or(Error::FailedToGetRandomBytes)?
|
||||
}
|
||||
_ => offered_key_share,
|
||||
};
|
||||
|
||||
Ok(emit_client_hello_for_retry(
|
||||
self.next.config,
|
||||
cx,
|
||||
self.next.resuming_session,
|
||||
self.next.random,
|
||||
self.next.using_ems,
|
||||
transcript_buffer,
|
||||
self.next.sent_tls13_fake_ccs,
|
||||
self.next.hello,
|
||||
Some(self.next.session_id),
|
||||
ClientSessionIdGenerators::default(),
|
||||
Some(hrr),
|
||||
self.next.server_name,
|
||||
Some(key_share),
|
||||
self.extra_exts,
|
||||
may_send_sct_list,
|
||||
Some(cs),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl State<ClientConnectionData> for ExpectServerHelloOrHelloRetryRequest {
|
||||
fn handle(self: Box<Self>, cx: &mut ClientContext<'_>, m: Message) -> NextStateOrError {
|
||||
match m.payload {
|
||||
MessagePayload::Handshake {
|
||||
parsed:
|
||||
HandshakeMessagePayload {
|
||||
payload: HandshakePayload::ServerHello(..),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => self
|
||||
.into_expect_server_hello()
|
||||
.handle(cx, m),
|
||||
MessagePayload::Handshake {
|
||||
parsed:
|
||||
HandshakeMessagePayload {
|
||||
payload: HandshakePayload::HelloRetryRequest(..),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => self.handle_hello_retry_request(cx, m),
|
||||
payload => Err(inappropriate_handshake_message(
|
||||
&payload,
|
||||
&[ContentType::Handshake],
|
||||
&[HandshakeType::ServerHello, HandshakeType::HelloRetryRequest],
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn send_cert_error_alert(common: &mut CommonState, err: Error) -> Error {
|
||||
match err {
|
||||
Error::InvalidCertificateEncoding => {
|
||||
common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
}
|
||||
Error::PeerMisbehavedError(_) => {
|
||||
common.send_fatal_alert(AlertDescription::IllegalParameter);
|
||||
}
|
||||
_ => {
|
||||
common.send_fatal_alert(AlertDescription::BadCertificate);
|
||||
}
|
||||
};
|
||||
|
||||
err
|
||||
}
|
||||
1127
third_party/rustls-fork-shadow-tls/src/client/tls12.rs
vendored
Normal file
1127
third_party/rustls-fork-shadow-tls/src/client/tls12.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
1191
third_party/rustls-fork-shadow-tls/src/client/tls13.rs
vendored
Normal file
1191
third_party/rustls-fork-shadow-tls/src/client/tls13.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
1466
third_party/rustls-fork-shadow-tls/src/conn.rs
vendored
Normal file
1466
third_party/rustls-fork-shadow-tls/src/conn.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
431
third_party/rustls-fork-shadow-tls/src/enums.rs
vendored
Normal file
431
third_party/rustls-fork-shadow-tls/src/enums.rs
vendored
Normal file
|
|
@ -0,0 +1,431 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
#![allow(missing_docs)]
|
||||
use crate::msgs::codec::{Codec, Reader};
|
||||
|
||||
enum_builder! {
|
||||
/// The `ProtocolVersion` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U16
|
||||
EnumName: ProtocolVersion;
|
||||
EnumVal{
|
||||
SSLv2 => 0x0200,
|
||||
SSLv3 => 0x0300,
|
||||
TLSv1_0 => 0x0301,
|
||||
TLSv1_1 => 0x0302,
|
||||
TLSv1_2 => 0x0303,
|
||||
TLSv1_3 => 0x0304,
|
||||
DTLSv1_0 => 0xFEFF,
|
||||
DTLSv1_2 => 0xFEFD,
|
||||
DTLSv1_3 => 0xFEFC
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `CipherSuite` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U16
|
||||
EnumName: CipherSuite;
|
||||
EnumVal{
|
||||
TLS_NULL_WITH_NULL_NULL => 0x0000,
|
||||
TLS_RSA_WITH_NULL_MD5 => 0x0001,
|
||||
TLS_RSA_WITH_NULL_SHA => 0x0002,
|
||||
TLS_RSA_EXPORT_WITH_RC4_40_MD5 => 0x0003,
|
||||
TLS_RSA_WITH_RC4_128_MD5 => 0x0004,
|
||||
TLS_RSA_WITH_RC4_128_SHA => 0x0005,
|
||||
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 => 0x0006,
|
||||
TLS_RSA_WITH_IDEA_CBC_SHA => 0x0007,
|
||||
TLS_RSA_EXPORT_WITH_DES40_CBC_SHA => 0x0008,
|
||||
TLS_RSA_WITH_DES_CBC_SHA => 0x0009,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA => 0x000a,
|
||||
TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA => 0x000b,
|
||||
TLS_DH_DSS_WITH_DES_CBC_SHA => 0x000c,
|
||||
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA => 0x000d,
|
||||
TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA => 0x000e,
|
||||
TLS_DH_RSA_WITH_DES_CBC_SHA => 0x000f,
|
||||
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA => 0x0010,
|
||||
TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA => 0x0011,
|
||||
TLS_DHE_DSS_WITH_DES_CBC_SHA => 0x0012,
|
||||
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA => 0x0013,
|
||||
TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA => 0x0014,
|
||||
TLS_DHE_RSA_WITH_DES_CBC_SHA => 0x0015,
|
||||
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA => 0x0016,
|
||||
TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 => 0x0017,
|
||||
TLS_DH_anon_WITH_RC4_128_MD5 => 0x0018,
|
||||
TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA => 0x0019,
|
||||
TLS_DH_anon_WITH_DES_CBC_SHA => 0x001a,
|
||||
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA => 0x001b,
|
||||
SSL_FORTEZZA_KEA_WITH_NULL_SHA => 0x001c,
|
||||
SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA => 0x001d,
|
||||
TLS_KRB5_WITH_DES_CBC_SHA_or_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA => 0x001e,
|
||||
TLS_KRB5_WITH_3DES_EDE_CBC_SHA => 0x001f,
|
||||
TLS_KRB5_WITH_RC4_128_SHA => 0x0020,
|
||||
TLS_KRB5_WITH_IDEA_CBC_SHA => 0x0021,
|
||||
TLS_KRB5_WITH_DES_CBC_MD5 => 0x0022,
|
||||
TLS_KRB5_WITH_3DES_EDE_CBC_MD5 => 0x0023,
|
||||
TLS_KRB5_WITH_RC4_128_MD5 => 0x0024,
|
||||
TLS_KRB5_WITH_IDEA_CBC_MD5 => 0x0025,
|
||||
TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA => 0x0026,
|
||||
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA => 0x0027,
|
||||
TLS_KRB5_EXPORT_WITH_RC4_40_SHA => 0x0028,
|
||||
TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 => 0x0029,
|
||||
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 => 0x002a,
|
||||
TLS_KRB5_EXPORT_WITH_RC4_40_MD5 => 0x002b,
|
||||
TLS_PSK_WITH_NULL_SHA => 0x002c,
|
||||
TLS_DHE_PSK_WITH_NULL_SHA => 0x002d,
|
||||
TLS_RSA_PSK_WITH_NULL_SHA => 0x002e,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA => 0x002f,
|
||||
TLS_DH_DSS_WITH_AES_128_CBC_SHA => 0x0030,
|
||||
TLS_DH_RSA_WITH_AES_128_CBC_SHA => 0x0031,
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_SHA => 0x0032,
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA => 0x0033,
|
||||
TLS_DH_anon_WITH_AES_128_CBC_SHA => 0x0034,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA => 0x0035,
|
||||
TLS_DH_DSS_WITH_AES_256_CBC_SHA => 0x0036,
|
||||
TLS_DH_RSA_WITH_AES_256_CBC_SHA => 0x0037,
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_SHA => 0x0038,
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_SHA => 0x0039,
|
||||
TLS_DH_anon_WITH_AES_256_CBC_SHA => 0x003a,
|
||||
TLS_RSA_WITH_NULL_SHA256 => 0x003b,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 => 0x003c,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA256 => 0x003d,
|
||||
TLS_DH_DSS_WITH_AES_128_CBC_SHA256 => 0x003e,
|
||||
TLS_DH_RSA_WITH_AES_128_CBC_SHA256 => 0x003f,
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 => 0x0040,
|
||||
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA => 0x0041,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA => 0x0042,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA => 0x0043,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA => 0x0044,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA => 0x0045,
|
||||
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA => 0x0046,
|
||||
TLS_ECDH_ECDSA_WITH_NULL_SHA_draft => 0x0047,
|
||||
TLS_ECDH_ECDSA_WITH_RC4_128_SHA_draft => 0x0048,
|
||||
TLS_ECDH_ECDSA_WITH_DES_CBC_SHA_draft => 0x0049,
|
||||
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA_draft => 0x004a,
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA_draft => 0x004b,
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA_draft => 0x004c,
|
||||
TLS_ECDH_ECNRA_WITH_DES_CBC_SHA_draft => 0x004d,
|
||||
TLS_ECDH_ECNRA_WITH_3DES_EDE_CBC_SHA_draft => 0x004e,
|
||||
TLS_ECMQV_ECDSA_NULL_SHA_draft => 0x004f,
|
||||
TLS_ECMQV_ECDSA_WITH_RC4_128_SHA_draft => 0x0050,
|
||||
TLS_ECMQV_ECDSA_WITH_DES_CBC_SHA_draft => 0x0051,
|
||||
TLS_ECMQV_ECDSA_WITH_3DES_EDE_CBC_SHA_draft => 0x0052,
|
||||
TLS_ECMQV_ECNRA_NULL_SHA_draft => 0x0053,
|
||||
TLS_ECMQV_ECNRA_WITH_RC4_128_SHA_draft => 0x0054,
|
||||
TLS_ECMQV_ECNRA_WITH_DES_CBC_SHA_draft => 0x0055,
|
||||
TLS_ECMQV_ECNRA_WITH_3DES_EDE_CBC_SHA_draft => 0x0056,
|
||||
TLS_ECDH_anon_NULL_WITH_SHA_draft => 0x0057,
|
||||
TLS_ECDH_anon_WITH_RC4_128_SHA_draft => 0x0058,
|
||||
TLS_ECDH_anon_WITH_DES_CBC_SHA_draft => 0x0059,
|
||||
TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA_draft => 0x005a,
|
||||
TLS_ECDH_anon_EXPORT_WITH_DES40_CBC_SHA_draft => 0x005b,
|
||||
TLS_ECDH_anon_EXPORT_WITH_RC4_40_SHA_draft => 0x005c,
|
||||
TLS_RSA_EXPORT1024_WITH_RC4_56_MD5 => 0x0060,
|
||||
TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 => 0x0061,
|
||||
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA => 0x0062,
|
||||
TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA => 0x0063,
|
||||
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA => 0x0064,
|
||||
TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA => 0x0065,
|
||||
TLS_DHE_DSS_WITH_RC4_128_SHA => 0x0066,
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 => 0x0067,
|
||||
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 => 0x0068,
|
||||
TLS_DH_RSA_WITH_AES_256_CBC_SHA256 => 0x0069,
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 => 0x006a,
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 => 0x006b,
|
||||
TLS_DH_anon_WITH_AES_128_CBC_SHA256 => 0x006c,
|
||||
TLS_DH_anon_WITH_AES_256_CBC_SHA256 => 0x006d,
|
||||
TLS_DHE_DSS_WITH_3DES_EDE_CBC_RMD => 0x0072,
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_RMD => 0x0073,
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_RMD => 0x0074,
|
||||
TLS_DHE_RSA_WITH_3DES_EDE_CBC_RMD => 0x0077,
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_RMD => 0x0078,
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_RMD => 0x0079,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_RMD => 0x007c,
|
||||
TLS_RSA_WITH_AES_128_CBC_RMD => 0x007d,
|
||||
TLS_RSA_WITH_AES_256_CBC_RMD => 0x007e,
|
||||
TLS_GOSTR341094_WITH_28147_CNT_IMIT => 0x0080,
|
||||
TLS_GOSTR341001_WITH_28147_CNT_IMIT => 0x0081,
|
||||
TLS_GOSTR341094_WITH_NULL_GOSTR3411 => 0x0082,
|
||||
TLS_GOSTR341001_WITH_NULL_GOSTR3411 => 0x0083,
|
||||
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA => 0x0084,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA => 0x0085,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA => 0x0086,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA => 0x0087,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA => 0x0088,
|
||||
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA => 0x0089,
|
||||
TLS_PSK_WITH_RC4_128_SHA => 0x008a,
|
||||
TLS_PSK_WITH_3DES_EDE_CBC_SHA => 0x008b,
|
||||
TLS_PSK_WITH_AES_128_CBC_SHA => 0x008c,
|
||||
TLS_PSK_WITH_AES_256_CBC_SHA => 0x008d,
|
||||
TLS_DHE_PSK_WITH_RC4_128_SHA => 0x008e,
|
||||
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA => 0x008f,
|
||||
TLS_DHE_PSK_WITH_AES_128_CBC_SHA => 0x0090,
|
||||
TLS_DHE_PSK_WITH_AES_256_CBC_SHA => 0x0091,
|
||||
TLS_RSA_PSK_WITH_RC4_128_SHA => 0x0092,
|
||||
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA => 0x0093,
|
||||
TLS_RSA_PSK_WITH_AES_128_CBC_SHA => 0x0094,
|
||||
TLS_RSA_PSK_WITH_AES_256_CBC_SHA => 0x0095,
|
||||
TLS_RSA_WITH_SEED_CBC_SHA => 0x0096,
|
||||
TLS_DH_DSS_WITH_SEED_CBC_SHA => 0x0097,
|
||||
TLS_DH_RSA_WITH_SEED_CBC_SHA => 0x0098,
|
||||
TLS_DHE_DSS_WITH_SEED_CBC_SHA => 0x0099,
|
||||
TLS_DHE_RSA_WITH_SEED_CBC_SHA => 0x009a,
|
||||
TLS_DH_anon_WITH_SEED_CBC_SHA => 0x009b,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 => 0x009c,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 => 0x009d,
|
||||
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 => 0x009e,
|
||||
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 => 0x009f,
|
||||
TLS_DH_RSA_WITH_AES_128_GCM_SHA256 => 0x00a0,
|
||||
TLS_DH_RSA_WITH_AES_256_GCM_SHA384 => 0x00a1,
|
||||
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 => 0x00a2,
|
||||
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 => 0x00a3,
|
||||
TLS_DH_DSS_WITH_AES_128_GCM_SHA256 => 0x00a4,
|
||||
TLS_DH_DSS_WITH_AES_256_GCM_SHA384 => 0x00a5,
|
||||
TLS_DH_anon_WITH_AES_128_GCM_SHA256 => 0x00a6,
|
||||
TLS_DH_anon_WITH_AES_256_GCM_SHA384 => 0x00a7,
|
||||
TLS_PSK_WITH_AES_128_GCM_SHA256 => 0x00a8,
|
||||
TLS_PSK_WITH_AES_256_GCM_SHA384 => 0x00a9,
|
||||
TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 => 0x00aa,
|
||||
TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 => 0x00ab,
|
||||
TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 => 0x00ac,
|
||||
TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 => 0x00ad,
|
||||
TLS_PSK_WITH_AES_128_CBC_SHA256 => 0x00ae,
|
||||
TLS_PSK_WITH_AES_256_CBC_SHA384 => 0x00af,
|
||||
TLS_PSK_WITH_NULL_SHA256 => 0x00b0,
|
||||
TLS_PSK_WITH_NULL_SHA384 => 0x00b1,
|
||||
TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 => 0x00b2,
|
||||
TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 => 0x00b3,
|
||||
TLS_DHE_PSK_WITH_NULL_SHA256 => 0x00b4,
|
||||
TLS_DHE_PSK_WITH_NULL_SHA384 => 0x00b5,
|
||||
TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 => 0x00b6,
|
||||
TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 => 0x00b7,
|
||||
TLS_RSA_PSK_WITH_NULL_SHA256 => 0x00b8,
|
||||
TLS_RSA_PSK_WITH_NULL_SHA384 => 0x00b9,
|
||||
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0x00ba,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 => 0x00bb,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0x00bc,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 => 0x00bd,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0x00be,
|
||||
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 => 0x00bf,
|
||||
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c0,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c1,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c2,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c3,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c4,
|
||||
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c5,
|
||||
TLS_EMPTY_RENEGOTIATION_INFO_SCSV => 0x00ff,
|
||||
TLS13_AES_128_GCM_SHA256 => 0x1301,
|
||||
TLS13_AES_256_GCM_SHA384 => 0x1302,
|
||||
TLS13_CHACHA20_POLY1305_SHA256 => 0x1303,
|
||||
TLS13_AES_128_CCM_SHA256 => 0x1304,
|
||||
TLS13_AES_128_CCM_8_SHA256 => 0x1305,
|
||||
TLS_ECDH_ECDSA_WITH_NULL_SHA => 0xc001,
|
||||
TLS_ECDH_ECDSA_WITH_RC4_128_SHA => 0xc002,
|
||||
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA => 0xc003,
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA => 0xc004,
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA => 0xc005,
|
||||
TLS_ECDHE_ECDSA_WITH_NULL_SHA => 0xc006,
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA => 0xc007,
|
||||
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA => 0xc008,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA => 0xc009,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA => 0xc00a,
|
||||
TLS_ECDH_RSA_WITH_NULL_SHA => 0xc00b,
|
||||
TLS_ECDH_RSA_WITH_RC4_128_SHA => 0xc00c,
|
||||
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA => 0xc00d,
|
||||
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA => 0xc00e,
|
||||
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA => 0xc00f,
|
||||
TLS_ECDHE_RSA_WITH_NULL_SHA => 0xc010,
|
||||
TLS_ECDHE_RSA_WITH_RC4_128_SHA => 0xc011,
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA => 0xc012,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA => 0xc013,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA => 0xc014,
|
||||
TLS_ECDH_anon_WITH_NULL_SHA => 0xc015,
|
||||
TLS_ECDH_anon_WITH_RC4_128_SHA => 0xc016,
|
||||
TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA => 0xc017,
|
||||
TLS_ECDH_anon_WITH_AES_128_CBC_SHA => 0xc018,
|
||||
TLS_ECDH_anon_WITH_AES_256_CBC_SHA => 0xc019,
|
||||
TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA => 0xc01a,
|
||||
TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA => 0xc01b,
|
||||
TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA => 0xc01c,
|
||||
TLS_SRP_SHA_WITH_AES_128_CBC_SHA => 0xc01d,
|
||||
TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA => 0xc01e,
|
||||
TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA => 0xc01f,
|
||||
TLS_SRP_SHA_WITH_AES_256_CBC_SHA => 0xc020,
|
||||
TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA => 0xc021,
|
||||
TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA => 0xc022,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 => 0xc023,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 => 0xc024,
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 => 0xc025,
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 => 0xc026,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 => 0xc027,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 => 0xc028,
|
||||
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 => 0xc029,
|
||||
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 => 0xc02a,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 => 0xc02b,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 => 0xc02c,
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 => 0xc02d,
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 => 0xc02e,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => 0xc02f,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => 0xc030,
|
||||
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 => 0xc031,
|
||||
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 => 0xc032,
|
||||
TLS_ECDHE_PSK_WITH_RC4_128_SHA => 0xc033,
|
||||
TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA => 0xc034,
|
||||
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA => 0xc035,
|
||||
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA => 0xc036,
|
||||
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 => 0xc037,
|
||||
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 => 0xc038,
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA => 0xc039,
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA256 => 0xc03a,
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA384 => 0xc03b,
|
||||
TLS_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc03c,
|
||||
TLS_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc03d,
|
||||
TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 => 0xc03e,
|
||||
TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 => 0xc03f,
|
||||
TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc040,
|
||||
TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc041,
|
||||
TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 => 0xc042,
|
||||
TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 => 0xc043,
|
||||
TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc044,
|
||||
TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc045,
|
||||
TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 => 0xc046,
|
||||
TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 => 0xc047,
|
||||
TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 => 0xc048,
|
||||
TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 => 0xc049,
|
||||
TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 => 0xc04a,
|
||||
TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 => 0xc04b,
|
||||
TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc04c,
|
||||
TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc04d,
|
||||
TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc04e,
|
||||
TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc04f,
|
||||
TLS_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc050,
|
||||
TLS_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc051,
|
||||
TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc052,
|
||||
TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc053,
|
||||
TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc054,
|
||||
TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc055,
|
||||
TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 => 0xc056,
|
||||
TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 => 0xc057,
|
||||
TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 => 0xc058,
|
||||
TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 => 0xc059,
|
||||
TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 => 0xc05a,
|
||||
TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 => 0xc05b,
|
||||
TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 => 0xc05c,
|
||||
TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 => 0xc05d,
|
||||
TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 => 0xc05e,
|
||||
TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 => 0xc05f,
|
||||
TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc060,
|
||||
TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc061,
|
||||
TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc062,
|
||||
TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc063,
|
||||
TLS_PSK_WITH_ARIA_128_CBC_SHA256 => 0xc064,
|
||||
TLS_PSK_WITH_ARIA_256_CBC_SHA384 => 0xc065,
|
||||
TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 => 0xc066,
|
||||
TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 => 0xc067,
|
||||
TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 => 0xc068,
|
||||
TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 => 0xc069,
|
||||
TLS_PSK_WITH_ARIA_128_GCM_SHA256 => 0xc06a,
|
||||
TLS_PSK_WITH_ARIA_256_GCM_SHA384 => 0xc06b,
|
||||
TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 => 0xc06c,
|
||||
TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 => 0xc06d,
|
||||
TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 => 0xc06e,
|
||||
TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 => 0xc06f,
|
||||
TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 => 0xc070,
|
||||
TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 => 0xc071,
|
||||
TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 => 0xc072,
|
||||
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 => 0xc073,
|
||||
TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 => 0xc074,
|
||||
TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 => 0xc075,
|
||||
TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0xc076,
|
||||
TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 => 0xc077,
|
||||
TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0xc078,
|
||||
TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 => 0xc079,
|
||||
TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc07a,
|
||||
TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc07b,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc07c,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc07d,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc07e,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc07f,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 => 0xc080,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 => 0xc081,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 => 0xc082,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 => 0xc083,
|
||||
TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 => 0xc084,
|
||||
TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 => 0xc085,
|
||||
TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc086,
|
||||
TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc087,
|
||||
TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc088,
|
||||
TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc089,
|
||||
TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc08a,
|
||||
TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc08b,
|
||||
TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc08c,
|
||||
TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc08d,
|
||||
TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 => 0xc08e,
|
||||
TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 => 0xc08f,
|
||||
TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 => 0xc090,
|
||||
TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 => 0xc091,
|
||||
TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 => 0xc092,
|
||||
TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 => 0xc093,
|
||||
TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 => 0xc094,
|
||||
TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 => 0xc095,
|
||||
TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 => 0xc096,
|
||||
TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 => 0xc097,
|
||||
TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 => 0xc098,
|
||||
TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 => 0xc099,
|
||||
TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 => 0xc09a,
|
||||
TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 => 0xc09b,
|
||||
TLS_RSA_WITH_AES_128_CCM => 0xc09c,
|
||||
TLS_RSA_WITH_AES_256_CCM => 0xc09d,
|
||||
TLS_DHE_RSA_WITH_AES_128_CCM => 0xc09e,
|
||||
TLS_DHE_RSA_WITH_AES_256_CCM => 0xc09f,
|
||||
TLS_RSA_WITH_AES_128_CCM_8 => 0xc0a0,
|
||||
TLS_RSA_WITH_AES_256_CCM_8 => 0xc0a1,
|
||||
TLS_DHE_RSA_WITH_AES_128_CCM_8 => 0xc0a2,
|
||||
TLS_DHE_RSA_WITH_AES_256_CCM_8 => 0xc0a3,
|
||||
TLS_PSK_WITH_AES_128_CCM => 0xc0a4,
|
||||
TLS_PSK_WITH_AES_256_CCM => 0xc0a5,
|
||||
TLS_DHE_PSK_WITH_AES_128_CCM => 0xc0a6,
|
||||
TLS_DHE_PSK_WITH_AES_256_CCM => 0xc0a7,
|
||||
TLS_PSK_WITH_AES_128_CCM_8 => 0xc0a8,
|
||||
TLS_PSK_WITH_AES_256_CCM_8 => 0xc0a9,
|
||||
TLS_PSK_DHE_WITH_AES_128_CCM_8 => 0xc0aa,
|
||||
TLS_PSK_DHE_WITH_AES_256_CCM_8 => 0xc0ab,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CCM => 0xc0ac,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CCM => 0xc0ad,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 => 0xc0ae,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 => 0xc0af,
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => 0xcca8,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => 0xcca9,
|
||||
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => 0xccaa,
|
||||
TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 => 0xccab,
|
||||
TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 => 0xccac,
|
||||
TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 => 0xccad,
|
||||
TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 => 0xccae,
|
||||
SSL_RSA_FIPS_WITH_DES_CBC_SHA => 0xfefe,
|
||||
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA => 0xfeff
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `SignatureScheme` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U16
|
||||
EnumName: SignatureScheme;
|
||||
EnumVal{
|
||||
RSA_PKCS1_SHA1 => 0x0201,
|
||||
ECDSA_SHA1_Legacy => 0x0203,
|
||||
RSA_PKCS1_SHA256 => 0x0401,
|
||||
ECDSA_NISTP256_SHA256 => 0x0403,
|
||||
RSA_PKCS1_SHA384 => 0x0501,
|
||||
ECDSA_NISTP384_SHA384 => 0x0503,
|
||||
RSA_PKCS1_SHA512 => 0x0601,
|
||||
ECDSA_NISTP521_SHA512 => 0x0603,
|
||||
RSA_PSS_SHA256 => 0x0804,
|
||||
RSA_PSS_SHA384 => 0x0805,
|
||||
RSA_PSS_SHA512 => 0x0806,
|
||||
ED25519 => 0x0807,
|
||||
ED448 => 0x0808
|
||||
}
|
||||
}
|
||||
245
third_party/rustls-fork-shadow-tls/src/error.rs
vendored
Normal file
245
third_party/rustls-fork-shadow-tls/src/error.rs
vendored
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
use crate::msgs::enums::{AlertDescription, ContentType, HandshakeType};
|
||||
use crate::rand;
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::time::SystemTimeError;
|
||||
|
||||
/// rustls reports protocol errors using this type.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Error {
|
||||
/// We received a TLS message that isn't valid right now.
|
||||
/// `expect_types` lists the message types we can expect right now.
|
||||
/// `got_type` is the type we found. This error is typically
|
||||
/// caused by a buggy TLS stack (the peer or this one), a broken
|
||||
/// network, or an attack.
|
||||
InappropriateMessage {
|
||||
/// Which types we expected
|
||||
expect_types: Vec<ContentType>,
|
||||
/// What type we received
|
||||
got_type: ContentType,
|
||||
},
|
||||
|
||||
/// We received a TLS handshake message that isn't valid right now.
|
||||
/// `expect_types` lists the handshake message types we can expect
|
||||
/// right now. `got_type` is the type we found.
|
||||
InappropriateHandshakeMessage {
|
||||
/// Which handshake type we expected
|
||||
expect_types: Vec<HandshakeType>,
|
||||
/// What handshake type we received
|
||||
got_type: HandshakeType,
|
||||
},
|
||||
|
||||
/// The peer sent us a syntactically incorrect TLS message.
|
||||
CorruptMessage,
|
||||
|
||||
/// The peer sent us a TLS message with invalid contents.
|
||||
CorruptMessagePayload(ContentType),
|
||||
|
||||
/// The peer didn't give us any certificates.
|
||||
NoCertificatesPresented,
|
||||
|
||||
/// The certificate verifier doesn't support the given type of name.
|
||||
UnsupportedNameType,
|
||||
|
||||
/// We couldn't decrypt a message. This is invariably fatal.
|
||||
DecryptError,
|
||||
|
||||
/// We couldn't encrypt a message because it was larger than the allowed message size.
|
||||
/// This should never happen if the application is using valid record sizes.
|
||||
EncryptError,
|
||||
|
||||
/// The peer doesn't support a protocol version/feature we require.
|
||||
/// The parameter gives a hint as to what version/feature it is.
|
||||
PeerIncompatibleError(String),
|
||||
|
||||
/// The peer deviated from the standard TLS protocol.
|
||||
/// The parameter gives a hint where.
|
||||
PeerMisbehavedError(String),
|
||||
|
||||
/// We received a fatal alert. This means the peer is unhappy.
|
||||
AlertReceived(AlertDescription),
|
||||
|
||||
/// We received an invalidly encoded certificate from the peer.
|
||||
InvalidCertificateEncoding,
|
||||
|
||||
/// We received a certificate with invalid signature type.
|
||||
InvalidCertificateSignatureType,
|
||||
|
||||
/// We received a certificate with invalid signature.
|
||||
InvalidCertificateSignature,
|
||||
|
||||
/// We received a certificate which includes invalid data.
|
||||
InvalidCertificateData(String),
|
||||
|
||||
/// The presented SCT(s) were invalid.
|
||||
InvalidSct(sct::Error),
|
||||
|
||||
/// A catch-all error for unlikely errors.
|
||||
General(String),
|
||||
|
||||
/// We failed to figure out what time it currently is.
|
||||
FailedToGetCurrentTime,
|
||||
|
||||
/// We failed to acquire random bytes from the system.
|
||||
FailedToGetRandomBytes,
|
||||
|
||||
/// This function doesn't work until the TLS handshake
|
||||
/// is complete.
|
||||
HandshakeNotComplete,
|
||||
|
||||
/// The peer sent an oversized record/fragment.
|
||||
PeerSentOversizedRecord,
|
||||
|
||||
/// An incoming connection did not support any known application protocol.
|
||||
NoApplicationProtocol,
|
||||
|
||||
/// The `max_fragment_size` value supplied in configuration was too small,
|
||||
/// or too large.
|
||||
BadMaxFragmentSize,
|
||||
}
|
||||
|
||||
fn join<T: fmt::Debug>(items: &[T]) -> String {
|
||||
items
|
||||
.iter()
|
||||
.map(|x| format!("{:?}", x))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" or ")
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Self::InappropriateMessage {
|
||||
ref expect_types,
|
||||
ref got_type,
|
||||
} => write!(
|
||||
f,
|
||||
"received unexpected message: got {:?} when expecting {}",
|
||||
got_type,
|
||||
join::<ContentType>(expect_types)
|
||||
),
|
||||
Self::InappropriateHandshakeMessage {
|
||||
ref expect_types,
|
||||
ref got_type,
|
||||
} => write!(
|
||||
f,
|
||||
"received unexpected handshake message: got {:?} when expecting {}",
|
||||
got_type,
|
||||
join::<HandshakeType>(expect_types)
|
||||
),
|
||||
Self::CorruptMessagePayload(ref typ) => {
|
||||
write!(f, "received corrupt message of type {:?}", typ)
|
||||
}
|
||||
Self::PeerIncompatibleError(ref why) => write!(f, "peer is incompatible: {}", why),
|
||||
Self::PeerMisbehavedError(ref why) => write!(f, "peer misbehaved: {}", why),
|
||||
Self::AlertReceived(ref alert) => write!(f, "received fatal alert: {:?}", alert),
|
||||
Self::InvalidCertificateEncoding => {
|
||||
write!(f, "invalid peer certificate encoding")
|
||||
}
|
||||
Self::InvalidCertificateSignatureType => {
|
||||
write!(f, "invalid peer certificate signature type")
|
||||
}
|
||||
Self::InvalidCertificateSignature => {
|
||||
write!(f, "invalid peer certificate signature")
|
||||
}
|
||||
Self::InvalidCertificateData(ref reason) => {
|
||||
write!(f, "invalid peer certificate contents: {}", reason)
|
||||
}
|
||||
Self::CorruptMessage => write!(f, "received corrupt message"),
|
||||
Self::NoCertificatesPresented => write!(f, "peer sent no certificates"),
|
||||
Self::UnsupportedNameType => write!(f, "presented server name type wasn't supported"),
|
||||
Self::DecryptError => write!(f, "cannot decrypt peer's message"),
|
||||
Self::EncryptError => write!(f, "cannot encrypt message"),
|
||||
Self::PeerSentOversizedRecord => write!(f, "peer sent excess record size"),
|
||||
Self::HandshakeNotComplete => write!(f, "handshake not complete"),
|
||||
Self::NoApplicationProtocol => write!(f, "peer doesn't support any known protocol"),
|
||||
Self::InvalidSct(ref err) => write!(f, "invalid certificate timestamp: {:?}", err),
|
||||
Self::FailedToGetCurrentTime => write!(f, "failed to get current time"),
|
||||
Self::FailedToGetRandomBytes => write!(f, "failed to get random bytes"),
|
||||
Self::BadMaxFragmentSize => {
|
||||
write!(f, "the supplied max_fragment_size was too small or large")
|
||||
}
|
||||
Self::General(ref err) => write!(f, "unexpected error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SystemTimeError> for Error {
|
||||
#[inline]
|
||||
fn from(_: SystemTimeError) -> Self {
|
||||
Self::FailedToGetCurrentTime
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {}
|
||||
|
||||
impl From<rand::GetRandomFailed> for Error {
|
||||
fn from(_: rand::GetRandomFailed) -> Self {
|
||||
Self::FailedToGetRandomBytes
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Error;
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
use crate::msgs::enums::{AlertDescription, ContentType, HandshakeType};
|
||||
use sct;
|
||||
|
||||
let all = vec![
|
||||
Error::InappropriateMessage {
|
||||
expect_types: vec![ContentType::Alert],
|
||||
got_type: ContentType::Handshake,
|
||||
},
|
||||
Error::InappropriateHandshakeMessage {
|
||||
expect_types: vec![HandshakeType::ClientHello, HandshakeType::Finished],
|
||||
got_type: HandshakeType::ServerHello,
|
||||
},
|
||||
Error::CorruptMessage,
|
||||
Error::CorruptMessagePayload(ContentType::Alert),
|
||||
Error::NoCertificatesPresented,
|
||||
Error::DecryptError,
|
||||
Error::PeerIncompatibleError("no tls1.2".to_string()),
|
||||
Error::PeerMisbehavedError("inconsistent something".to_string()),
|
||||
Error::AlertReceived(AlertDescription::ExportRestriction),
|
||||
Error::InvalidCertificateEncoding,
|
||||
Error::InvalidCertificateSignatureType,
|
||||
Error::InvalidCertificateSignature,
|
||||
Error::InvalidCertificateData("Data".into()),
|
||||
Error::InvalidSct(sct::Error::MalformedSct),
|
||||
Error::General("undocumented error".to_string()),
|
||||
Error::FailedToGetCurrentTime,
|
||||
Error::FailedToGetRandomBytes,
|
||||
Error::HandshakeNotComplete,
|
||||
Error::PeerSentOversizedRecord,
|
||||
Error::NoApplicationProtocol,
|
||||
Error::BadMaxFragmentSize,
|
||||
];
|
||||
|
||||
for err in all {
|
||||
println!("{:?}:", err);
|
||||
println!(" fmt '{}'", err);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rand_error_mapping() {
|
||||
use super::rand;
|
||||
let err: Error = rand::GetRandomFailed.into();
|
||||
assert_eq!(err, Error::FailedToGetRandomBytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn time_error_mapping() {
|
||||
use std::time::SystemTime;
|
||||
|
||||
let time_error = SystemTime::UNIX_EPOCH
|
||||
.duration_since(SystemTime::now())
|
||||
.unwrap_err();
|
||||
let err: Error = time_error.into();
|
||||
assert_eq!(err, Error::FailedToGetCurrentTime);
|
||||
}
|
||||
}
|
||||
240
third_party/rustls-fork-shadow-tls/src/hash_hs.rs
vendored
Normal file
240
third_party/rustls-fork-shadow-tls/src/hash_hs.rs
vendored
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
use crate::msgs::codec::Codec;
|
||||
use crate::msgs::handshake::HandshakeMessagePayload;
|
||||
use crate::msgs::message::{Message, MessagePayload};
|
||||
use ring::digest;
|
||||
use std::mem;
|
||||
|
||||
/// Early stage buffering of handshake payloads.
|
||||
///
|
||||
/// Before we know the hash algorithm to use to verify the handshake, we just buffer the messages.
|
||||
/// During the handshake, we may restart the transcript due to a HelloRetryRequest, reverting
|
||||
/// from the `HandshakeHash` to a `HandshakeHashBuffer` again.
|
||||
pub(crate) struct HandshakeHashBuffer {
|
||||
buffer: Vec<u8>,
|
||||
client_auth_enabled: bool,
|
||||
}
|
||||
|
||||
impl HandshakeHashBuffer {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
buffer: Vec::new(),
|
||||
client_auth_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// We might be doing client auth, so need to keep a full
|
||||
/// log of the handshake.
|
||||
pub(crate) fn set_client_auth_enabled(&mut self) {
|
||||
self.client_auth_enabled = true;
|
||||
}
|
||||
|
||||
/// Hash/buffer a handshake message.
|
||||
pub(crate) fn add_message(&mut self, m: &Message) {
|
||||
if let MessagePayload::Handshake { encoded, .. } = &m.payload {
|
||||
self.buffer
|
||||
.extend_from_slice(&encoded.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash or buffer a byte slice.
|
||||
#[cfg(test)]
|
||||
fn update_raw(&mut self, buf: &[u8]) {
|
||||
self.buffer.extend_from_slice(buf);
|
||||
}
|
||||
|
||||
/// Get the hash value if we were to hash `extra` too.
|
||||
pub(crate) fn get_hash_given(
|
||||
&self,
|
||||
hash: &'static digest::Algorithm,
|
||||
extra: &[u8],
|
||||
) -> digest::Digest {
|
||||
let mut ctx = digest::Context::new(hash);
|
||||
ctx.update(&self.buffer);
|
||||
ctx.update(extra);
|
||||
ctx.finish()
|
||||
}
|
||||
|
||||
/// We now know what hash function the verify_data will use.
|
||||
pub(crate) fn start_hash(self, alg: &'static digest::Algorithm) -> HandshakeHash {
|
||||
let mut ctx = digest::Context::new(alg);
|
||||
ctx.update(&self.buffer);
|
||||
HandshakeHash {
|
||||
ctx,
|
||||
client_auth: match self.client_auth_enabled {
|
||||
true => Some(self.buffer),
|
||||
false => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This deals with keeping a running hash of the handshake
|
||||
/// payloads. This is computed by buffering initially. Once
|
||||
/// we know what hash function we need to use we switch to
|
||||
/// incremental hashing.
|
||||
///
|
||||
/// For client auth, we also need to buffer all the messages.
|
||||
/// This is disabled in cases where client auth is not possible.
|
||||
pub(crate) struct HandshakeHash {
|
||||
/// None before we know what hash function we're using
|
||||
ctx: digest::Context,
|
||||
|
||||
/// buffer for client-auth.
|
||||
client_auth: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl HandshakeHash {
|
||||
/// We decided not to do client auth after all, so discard
|
||||
/// the transcript.
|
||||
pub(crate) fn abandon_client_auth(&mut self) {
|
||||
self.client_auth = None;
|
||||
}
|
||||
|
||||
/// Hash/buffer a handshake message.
|
||||
pub(crate) fn add_message(&mut self, m: &Message) -> &mut Self {
|
||||
if let MessagePayload::Handshake { encoded, .. } = &m.payload {
|
||||
self.update_raw(&encoded.0);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Hash or buffer a byte slice.
|
||||
fn update_raw(&mut self, buf: &[u8]) -> &mut Self {
|
||||
self.ctx.update(buf);
|
||||
|
||||
if let Some(buffer) = &mut self.client_auth {
|
||||
buffer.extend_from_slice(buf);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the hash value if we were to hash `extra` too,
|
||||
/// using hash function `hash`.
|
||||
pub(crate) fn get_hash_given(&self, extra: &[u8]) -> digest::Digest {
|
||||
let mut ctx = self.ctx.clone();
|
||||
ctx.update(extra);
|
||||
ctx.finish()
|
||||
}
|
||||
|
||||
pub(crate) fn into_hrr_buffer(self) -> HandshakeHashBuffer {
|
||||
let old_hash = self.ctx.finish();
|
||||
let old_handshake_hash_msg =
|
||||
HandshakeMessagePayload::build_handshake_hash(old_hash.as_ref());
|
||||
|
||||
HandshakeHashBuffer {
|
||||
client_auth_enabled: self.client_auth.is_some(),
|
||||
buffer: old_handshake_hash_msg.get_encoding(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the current hash value, and encapsulate it in a
|
||||
/// 'handshake_hash' handshake message. Start this hash
|
||||
/// again, with that message at the front.
|
||||
pub(crate) fn rollup_for_hrr(&mut self) {
|
||||
let ctx = &mut self.ctx;
|
||||
|
||||
let old_ctx = mem::replace(ctx, digest::Context::new(ctx.algorithm()));
|
||||
let old_hash = old_ctx.finish();
|
||||
let old_handshake_hash_msg =
|
||||
HandshakeMessagePayload::build_handshake_hash(old_hash.as_ref());
|
||||
|
||||
self.update_raw(&old_handshake_hash_msg.get_encoding());
|
||||
}
|
||||
|
||||
/// Get the current hash value.
|
||||
pub(crate) fn get_current_hash(&self) -> digest::Digest {
|
||||
self.ctx.clone().finish()
|
||||
}
|
||||
|
||||
/// Takes this object's buffer containing all handshake messages
|
||||
/// so far. This method only works once; it resets the buffer
|
||||
/// to empty.
|
||||
#[cfg(feature = "tls12")]
|
||||
pub(crate) fn take_handshake_buf(&mut self) -> Option<Vec<u8>> {
|
||||
self.client_auth.take()
|
||||
}
|
||||
|
||||
/// The digest algorithm
|
||||
pub(crate) fn algorithm(&self) -> &'static digest::Algorithm {
|
||||
self.ctx.algorithm()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::HandshakeHashBuffer;
|
||||
use ring::digest;
|
||||
|
||||
#[test]
|
||||
fn hashes_correctly() {
|
||||
let mut hhb = HandshakeHashBuffer::new();
|
||||
hhb.update_raw(b"hello");
|
||||
assert_eq!(hhb.buffer.len(), 5);
|
||||
let mut hh = hhb.start_hash(&digest::SHA256);
|
||||
assert!(hh.client_auth.is_none());
|
||||
hh.update_raw(b"world");
|
||||
let h = hh.get_current_hash();
|
||||
let h = h.as_ref();
|
||||
assert_eq!(h[0], 0x93);
|
||||
assert_eq!(h[1], 0x6a);
|
||||
assert_eq!(h[2], 0x18);
|
||||
assert_eq!(h[3], 0x5c);
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
#[test]
|
||||
fn buffers_correctly() {
|
||||
let mut hhb = HandshakeHashBuffer::new();
|
||||
hhb.set_client_auth_enabled();
|
||||
hhb.update_raw(b"hello");
|
||||
assert_eq!(hhb.buffer.len(), 5);
|
||||
let mut hh = hhb.start_hash(&digest::SHA256);
|
||||
assert_eq!(
|
||||
hh.client_auth
|
||||
.as_ref()
|
||||
.map(|buf| buf.len()),
|
||||
Some(5)
|
||||
);
|
||||
hh.update_raw(b"world");
|
||||
assert_eq!(
|
||||
hh.client_auth
|
||||
.as_ref()
|
||||
.map(|buf| buf.len()),
|
||||
Some(10)
|
||||
);
|
||||
let h = hh.get_current_hash();
|
||||
let h = h.as_ref();
|
||||
assert_eq!(h[0], 0x93);
|
||||
assert_eq!(h[1], 0x6a);
|
||||
assert_eq!(h[2], 0x18);
|
||||
assert_eq!(h[3], 0x5c);
|
||||
let buf = hh.take_handshake_buf();
|
||||
assert_eq!(Some(b"helloworld".to_vec()), buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abandon() {
|
||||
let mut hhb = HandshakeHashBuffer::new();
|
||||
hhb.set_client_auth_enabled();
|
||||
hhb.update_raw(b"hello");
|
||||
assert_eq!(hhb.buffer.len(), 5);
|
||||
let mut hh = hhb.start_hash(&digest::SHA256);
|
||||
assert_eq!(
|
||||
hh.client_auth
|
||||
.as_ref()
|
||||
.map(|buf| buf.len()),
|
||||
Some(5)
|
||||
);
|
||||
hh.abandon_client_auth();
|
||||
assert_eq!(hh.client_auth, None);
|
||||
hh.update_raw(b"world");
|
||||
assert_eq!(hh.client_auth, None);
|
||||
let h = hh.get_current_hash();
|
||||
let h = h.as_ref();
|
||||
assert_eq!(h[0], 0x93);
|
||||
assert_eq!(h[1], 0x6a);
|
||||
assert_eq!(h[2], 0x18);
|
||||
assert_eq!(h[3], 0x5c);
|
||||
}
|
||||
}
|
||||
52
third_party/rustls-fork-shadow-tls/src/key.rs
vendored
Normal file
52
third_party/rustls-fork-shadow-tls/src/key.rs
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
use std::fmt;
|
||||
|
||||
/// This type contains a private key by value.
|
||||
///
|
||||
/// The private key must be DER-encoded ASN.1 in either
|
||||
/// PKCS#8 or PKCS#1 format.
|
||||
///
|
||||
/// The `rustls-pemfile` crate can be used to extract
|
||||
/// private keys from a PEM file in these formats.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct PrivateKey(pub Vec<u8>);
|
||||
|
||||
/// This type contains a single certificate by value.
|
||||
///
|
||||
/// The certificate must be DER-encoded X.509.
|
||||
///
|
||||
/// The `rustls-pemfile` crate can be used to parse a PEM file.
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// If you are receiving certificates from an untrusted client or server, the contents
|
||||
/// must be validated manually.
|
||||
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Certificate(pub Vec<u8>);
|
||||
|
||||
impl AsRef<[u8]> for Certificate {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Certificate {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use super::bs_debug::BsDebug;
|
||||
f.debug_tuple("Certificate")
|
||||
.field(&BsDebug(&self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Certificate;
|
||||
|
||||
#[test]
|
||||
fn certificate_debug() {
|
||||
assert_eq!(
|
||||
"Certificate(b\"ab\")",
|
||||
format!("{:?}", Certificate(b"ab".to_vec()))
|
||||
);
|
||||
}
|
||||
}
|
||||
55
third_party/rustls-fork-shadow-tls/src/key_log.rs
vendored
Normal file
55
third_party/rustls-fork-shadow-tls/src/key_log.rs
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/// This trait represents the ability to do something useful
|
||||
/// with key material, such as logging it to a file for debugging.
|
||||
///
|
||||
/// Naturally, secrets passed over the interface are *extremely*
|
||||
/// sensitive and can break the security of past, present and
|
||||
/// future sessions.
|
||||
///
|
||||
/// You'll likely want some interior mutability in your
|
||||
/// implementation to make this useful.
|
||||
///
|
||||
/// See [`KeyLogFile`](crate::KeyLogFile) that implements the standard
|
||||
/// `SSLKEYLOGFILE` environment variable behaviour.
|
||||
pub trait KeyLog: Send + Sync {
|
||||
/// Log the given `secret`. `client_random` is provided for
|
||||
/// session identification. `label` describes precisely what
|
||||
/// `secret` means:
|
||||
///
|
||||
/// - `CLIENT_RANDOM`: `secret` is the master secret for a TLSv1.2 session.
|
||||
/// - `CLIENT_EARLY_TRAFFIC_SECRET`: `secret` encrypts early data
|
||||
/// transmitted by a client
|
||||
/// - `SERVER_HANDSHAKE_TRAFFIC_SECRET`: `secret` encrypts
|
||||
/// handshake messages from the server during a TLSv1.3 handshake.
|
||||
/// - `CLIENT_HANDSHAKE_TRAFFIC_SECRET`: `secret` encrypts
|
||||
/// handshake messages from the client during a TLSv1.3 handshake.
|
||||
/// - `SERVER_TRAFFIC_SECRET_0`: `secret` encrypts post-handshake data
|
||||
/// from the server in a TLSv1.3 session.
|
||||
/// - `CLIENT_TRAFFIC_SECRET_0`: `secret` encrypts post-handshake data
|
||||
/// from the client in a TLSv1.3 session.
|
||||
/// - `EXPORTER_SECRET`: `secret` is the post-handshake exporter secret
|
||||
/// in a TLSv1.3 session.
|
||||
///
|
||||
/// These strings are selected to match the NSS key log format:
|
||||
/// <https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format>
|
||||
fn log(&self, label: &str, client_random: &[u8], secret: &[u8]);
|
||||
|
||||
/// Indicates whether the secret with label `label` will be logged.
|
||||
///
|
||||
/// If `will_log` returns true then `log` will be called with the secret.
|
||||
/// Otherwise, `log` will not be called for the secret. This is a
|
||||
/// performance optimization.
|
||||
fn will_log(&self, _label: &str) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// KeyLog that does exactly nothing.
|
||||
pub struct NoKeyLog;
|
||||
|
||||
impl KeyLog for NoKeyLog {
|
||||
fn log(&self, _: &str, _: &[u8], _: &[u8]) {}
|
||||
#[inline]
|
||||
fn will_log(&self, _label: &str) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
154
third_party/rustls-fork-shadow-tls/src/key_log_file.rs
vendored
Normal file
154
third_party/rustls-fork-shadow-tls/src/key_log_file.rs
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#[cfg(feature = "logging")]
|
||||
use crate::log::warn;
|
||||
use crate::KeyLog;
|
||||
use std::env;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
|
||||
// Internal mutable state for KeyLogFile
|
||||
struct KeyLogFileInner {
|
||||
file: Option<File>,
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl KeyLogFileInner {
|
||||
fn new(var: Result<String, env::VarError>) -> Self {
|
||||
let path = match var {
|
||||
Ok(ref s) => Path::new(s),
|
||||
Err(env::VarError::NotUnicode(ref s)) => Path::new(s),
|
||||
Err(env::VarError::NotPresent) => {
|
||||
return Self {
|
||||
file: None,
|
||||
buf: Vec::new(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
|
||||
let file = match OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(path)
|
||||
{
|
||||
Ok(f) => Some(f),
|
||||
Err(e) => {
|
||||
warn!("unable to create key log file {:?}: {}", path, e);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
file,
|
||||
buf: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_write(&mut self, label: &str, client_random: &[u8], secret: &[u8]) -> io::Result<()> {
|
||||
let mut file = match self.file {
|
||||
None => {
|
||||
return Ok(());
|
||||
}
|
||||
Some(ref f) => f,
|
||||
};
|
||||
|
||||
self.buf.truncate(0);
|
||||
write!(self.buf, "{} ", label)?;
|
||||
for b in client_random.iter() {
|
||||
write!(self.buf, "{:02x}", b)?;
|
||||
}
|
||||
write!(self.buf, " ")?;
|
||||
for b in secret.iter() {
|
||||
write!(self.buf, "{:02x}", b)?;
|
||||
}
|
||||
writeln!(self.buf)?;
|
||||
file.write_all(&self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`KeyLog`] implementation that opens a file whose name is
|
||||
/// given by the `SSLKEYLOGFILE` environment variable, and writes
|
||||
/// keys into it.
|
||||
///
|
||||
/// If `SSLKEYLOGFILE` is not set, this does nothing.
|
||||
///
|
||||
/// If such a file cannot be opened, or cannot be written then
|
||||
/// this does nothing but logs errors at warning-level.
|
||||
pub struct KeyLogFile(Mutex<KeyLogFileInner>);
|
||||
|
||||
impl KeyLogFile {
|
||||
/// Makes a new `KeyLogFile`. The environment variable is
|
||||
/// inspected and the named file is opened during this call.
|
||||
pub fn new() -> Self {
|
||||
let var = env::var("SSLKEYLOGFILE");
|
||||
Self(Mutex::new(KeyLogFileInner::new(var)))
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyLog for KeyLogFile {
|
||||
fn log(&self, label: &str, client_random: &[u8], secret: &[u8]) {
|
||||
#[cfg_attr(not(feature = "logging"), allow(unused_variables))]
|
||||
match self
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.try_write(label, client_random, secret)
|
||||
{
|
||||
Ok(()) => {}
|
||||
Err(e) => {
|
||||
warn!("error writing to key log file: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, target_os = "linux"))]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn init() {
|
||||
let _ = env_logger::builder()
|
||||
.is_test(true)
|
||||
.try_init();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var_is_not_unicode() {
|
||||
init();
|
||||
let mut inner = KeyLogFileInner::new(Err(env::VarError::NotUnicode(
|
||||
"/tmp/keylogfileinnertest".into(),
|
||||
)));
|
||||
assert!(inner
|
||||
.try_write("label", b"random", b"secret")
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var_is_not_set() {
|
||||
init();
|
||||
let mut inner = KeyLogFileInner::new(Err(env::VarError::NotPresent));
|
||||
assert!(inner
|
||||
.try_write("label", b"random", b"secret")
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var_cannot_be_opened() {
|
||||
init();
|
||||
let mut inner = KeyLogFileInner::new(Ok("/dev/does-not-exist".into()));
|
||||
assert!(inner
|
||||
.try_write("label", b"random", b"secret")
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var_cannot_be_written() {
|
||||
init();
|
||||
let mut inner = KeyLogFileInner::new(Ok("/dev/full".into()));
|
||||
assert!(inner
|
||||
.try_write("label", b"random", b"secret")
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
100
third_party/rustls-fork-shadow-tls/src/kx.rs
vendored
Normal file
100
third_party/rustls-fork-shadow-tls/src/kx.rs
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use std::fmt;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::msgs::enums::NamedGroup;
|
||||
|
||||
/// An in-progress key exchange. This has the algorithm,
|
||||
/// our private key, and our public key.
|
||||
pub(crate) struct KeyExchange {
|
||||
skxg: &'static SupportedKxGroup,
|
||||
privkey: ring::agreement::EphemeralPrivateKey,
|
||||
pub(crate) pubkey: ring::agreement::PublicKey,
|
||||
}
|
||||
|
||||
impl KeyExchange {
|
||||
/// Choose a SupportedKxGroup by name, from a list of supported groups.
|
||||
pub(crate) fn choose(
|
||||
name: NamedGroup,
|
||||
supported: &[&'static SupportedKxGroup],
|
||||
) -> Option<&'static SupportedKxGroup> {
|
||||
supported
|
||||
.iter()
|
||||
.find(|skxg| skxg.name == name)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Start a key exchange, using the given SupportedKxGroup.
|
||||
///
|
||||
/// This generates an ephemeral key pair and stores it in the returned KeyExchange object.
|
||||
pub(crate) fn start(skxg: &'static SupportedKxGroup) -> Option<Self> {
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
let ours =
|
||||
ring::agreement::EphemeralPrivateKey::generate(skxg.agreement_algorithm, &rng).ok()?;
|
||||
|
||||
let pubkey = ours.compute_public_key().ok()?;
|
||||
|
||||
Some(Self {
|
||||
skxg,
|
||||
privkey: ours,
|
||||
pubkey,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the group being used.
|
||||
pub(crate) fn group(&self) -> NamedGroup {
|
||||
self.skxg.name
|
||||
}
|
||||
|
||||
/// Completes the key exchange, given the peer's public key.
|
||||
///
|
||||
/// The shared secret is passed into the closure passed down in `f`, and the result of calling
|
||||
/// `f` is returned to the caller.
|
||||
pub(crate) fn complete<T>(
|
||||
self,
|
||||
peer: &[u8],
|
||||
f: impl FnOnce(&[u8]) -> Result<T, ()>,
|
||||
) -> Result<T, Error> {
|
||||
let peer_key = ring::agreement::UnparsedPublicKey::new(self.skxg.agreement_algorithm, peer);
|
||||
ring::agreement::agree_ephemeral(self.privkey, &peer_key, (), f)
|
||||
.map_err(|()| Error::PeerMisbehavedError("key agreement failed".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
/// A key-exchange group supported by rustls.
|
||||
///
|
||||
/// All possible instances of this class are provided by the library in
|
||||
/// the `ALL_KX_GROUPS` array.
|
||||
pub struct SupportedKxGroup {
|
||||
/// The IANA "TLS Supported Groups" name of the group
|
||||
pub name: NamedGroup,
|
||||
|
||||
/// The corresponding ring agreement::Algorithm
|
||||
agreement_algorithm: &'static ring::agreement::Algorithm,
|
||||
}
|
||||
|
||||
impl fmt::Debug for SupportedKxGroup {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.name.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ephemeral ECDH on curve25519 (see RFC7748)
|
||||
pub static X25519: SupportedKxGroup = SupportedKxGroup {
|
||||
name: NamedGroup::X25519,
|
||||
agreement_algorithm: &ring::agreement::X25519,
|
||||
};
|
||||
|
||||
/// Ephemeral ECDH on secp256r1 (aka NIST-P256)
|
||||
pub static SECP256R1: SupportedKxGroup = SupportedKxGroup {
|
||||
name: NamedGroup::secp256r1,
|
||||
agreement_algorithm: &ring::agreement::ECDH_P256,
|
||||
};
|
||||
|
||||
/// Ephemeral ECDH on secp384r1 (aka NIST-P384)
|
||||
pub static SECP384R1: SupportedKxGroup = SupportedKxGroup {
|
||||
name: NamedGroup::secp384r1,
|
||||
agreement_algorithm: &ring::agreement::ECDH_P384,
|
||||
};
|
||||
|
||||
/// A list of all the key exchange groups supported by rustls.
|
||||
pub static ALL_KX_GROUPS: [&SupportedKxGroup; 3] = [&X25519, &SECP256R1, &SECP384R1];
|
||||
534
third_party/rustls-fork-shadow-tls/src/lib.rs
vendored
Normal file
534
third_party/rustls-fork-shadow-tls/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,534 @@
|
|||
//! # Rustls - a modern TLS library
|
||||
//! Rustls is a TLS library that aims to provide a good level of cryptographic security,
|
||||
//! requires no configuration to achieve that security, and provides no unsafe features or
|
||||
//! obsolete cryptography.
|
||||
//!
|
||||
//! ## Current features
|
||||
//!
|
||||
//! * TLS1.2 and TLS1.3.
|
||||
//! * ECDSA, Ed25519 or RSA server authentication by clients.
|
||||
//! * ECDSA, Ed25519 or RSA server authentication by servers.
|
||||
//! * Forward secrecy using ECDHE; with curve25519, nistp256 or nistp384 curves.
|
||||
//! * AES128-GCM and AES256-GCM bulk encryption, with safe nonces.
|
||||
//! * ChaCha20-Poly1305 bulk encryption ([RFC7905](https://tools.ietf.org/html/rfc7905)).
|
||||
//! * ALPN support.
|
||||
//! * SNI support.
|
||||
//! * Tunable fragment size to make TLS messages match size of underlying transport.
|
||||
//! * Optional use of vectored IO to minimise system calls.
|
||||
//! * TLS1.2 session resumption.
|
||||
//! * TLS1.2 resumption via tickets ([RFC5077](https://tools.ietf.org/html/rfc5077)).
|
||||
//! * TLS1.3 resumption via tickets or session storage.
|
||||
//! * TLS1.3 0-RTT data for clients.
|
||||
//! * TLS1.3 0-RTT data for servers.
|
||||
//! * Client authentication by clients.
|
||||
//! * Client authentication by servers.
|
||||
//! * Extended master secret support ([RFC7627](https://tools.ietf.org/html/rfc7627)).
|
||||
//! * Exporters ([RFC5705](https://tools.ietf.org/html/rfc5705)).
|
||||
//! * OCSP stapling by servers.
|
||||
//! * SCT stapling by servers.
|
||||
//! * SCT verification by clients.
|
||||
//!
|
||||
//! ## Possible future features
|
||||
//!
|
||||
//! * PSK support.
|
||||
//! * OCSP verification by clients.
|
||||
//! * Certificate pinning.
|
||||
//!
|
||||
//! ## Non-features
|
||||
//!
|
||||
//! For reasons [explained in the manual](manual),
|
||||
//! rustls does not and will not support:
|
||||
//!
|
||||
//! * SSL1, SSL2, SSL3, TLS1 or TLS1.1.
|
||||
//! * RC4.
|
||||
//! * DES or triple DES.
|
||||
//! * EXPORT ciphersuites.
|
||||
//! * MAC-then-encrypt ciphersuites.
|
||||
//! * Ciphersuites without forward secrecy.
|
||||
//! * Renegotiation.
|
||||
//! * Kerberos.
|
||||
//! * Compression.
|
||||
//! * Discrete-log Diffie-Hellman.
|
||||
//! * Automatic protocol version downgrade.
|
||||
//!
|
||||
//! There are plenty of other libraries that provide these features should you
|
||||
//! need them.
|
||||
//!
|
||||
//! ### Platform support
|
||||
//!
|
||||
//! Rustls uses [`ring`](https://crates.io/crates/ring) for implementing the
|
||||
//! cryptography in TLS. As a result, rustls only runs on platforms
|
||||
//! [supported by `ring`](https://github.com/briansmith/ring#online-automated-testing).
|
||||
//! At the time of writing this means x86, x86-64, armv7, and aarch64.
|
||||
//!
|
||||
//! ## Design Overview
|
||||
//! ### Rustls does not take care of network IO
|
||||
//! It doesn't make or accept TCP connections, or do DNS, or read or write files.
|
||||
//!
|
||||
//! There's example client and server code which uses mio to do all needed network
|
||||
//! IO.
|
||||
//!
|
||||
//! ### Rustls provides encrypted pipes
|
||||
//! These are the [`ServerConnection`] and [`ClientConnection`] types. You supply raw TLS traffic
|
||||
//! on the left (via the [`read_tls()`] and [`write_tls()`] methods) and then read/write the
|
||||
//! plaintext on the right:
|
||||
//!
|
||||
//! [`read_tls()`]: Connection::read_tls
|
||||
//! [`write_tls()`]: Connection::read_tls
|
||||
//!
|
||||
//! ```text
|
||||
//! TLS Plaintext
|
||||
//! === =========
|
||||
//! read_tls() +-----------------------+ reader() as io::Read
|
||||
//! | |
|
||||
//! +---------> ClientConnection +--------->
|
||||
//! | or |
|
||||
//! <---------+ ServerConnection <---------+
|
||||
//! | |
|
||||
//! write_tls() +-----------------------+ writer() as io::Write
|
||||
//! ```
|
||||
//!
|
||||
//! ### Rustls takes care of server certificate verification
|
||||
//! You do not need to provide anything other than a set of root certificates to trust.
|
||||
//! Certificate verification cannot be turned off or disabled in the main API.
|
||||
//!
|
||||
//! ## Getting started
|
||||
//! This is the minimum you need to do to make a TLS client connection.
|
||||
//!
|
||||
//! First we load some root certificates. These are used to authenticate the server.
|
||||
//! The recommended way is to depend on the `webpki_roots` crate which contains
|
||||
//! the Mozilla set of root certificates.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! let mut root_store = rustls::RootCertStore::empty();
|
||||
//! root_store.add_server_trust_anchors(
|
||||
//! webpki_roots::TLS_SERVER_ROOTS
|
||||
//! .0
|
||||
//! .iter()
|
||||
//! .map(|ta| {
|
||||
//! rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
//! ta.subject,
|
||||
//! ta.spki,
|
||||
//! ta.name_constraints,
|
||||
//! )
|
||||
//! })
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! Next, we make a `ClientConfig`. You're likely to make one of these per process,
|
||||
//! and use it for all connections made by that process.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! # let root_store: rustls::RootCertStore = panic!();
|
||||
//! let config = rustls::ClientConfig::builder()
|
||||
//! .with_safe_defaults()
|
||||
//! .with_root_certificates(root_store)
|
||||
//! .with_no_client_auth();
|
||||
//! ```
|
||||
//!
|
||||
//! Now we can make a connection. You need to provide the server's hostname so we
|
||||
//! know what to expect to find in the server's certificate.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use rustls;
|
||||
//! # use webpki;
|
||||
//! # use std::sync::Arc;
|
||||
//! # use std::convert::TryInto;
|
||||
//! # let mut root_store = rustls::RootCertStore::empty();
|
||||
//! # root_store.add_server_trust_anchors(
|
||||
//! # webpki_roots::TLS_SERVER_ROOTS
|
||||
//! # .0
|
||||
//! # .iter()
|
||||
//! # .map(|ta| {
|
||||
//! # rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
//! # ta.subject,
|
||||
//! # ta.spki,
|
||||
//! # ta.name_constraints,
|
||||
//! # )
|
||||
//! # })
|
||||
//! # );
|
||||
//! # let config = rustls::ClientConfig::builder()
|
||||
//! # .with_safe_defaults()
|
||||
//! # .with_root_certificates(root_store)
|
||||
//! # .with_no_client_auth();
|
||||
//! let rc_config = Arc::new(config);
|
||||
//! let example_com = "example.com".try_into().unwrap();
|
||||
//! let mut client = rustls::ClientConnection::new(rc_config, example_com);
|
||||
//! ```
|
||||
//!
|
||||
//! Now you should do appropriate IO for the `client` object. If `client.wants_read()` yields
|
||||
//! true, you should call `client.read_tls()` when the underlying connection has data.
|
||||
//! Likewise, if `client.wants_write()` yields true, you should call `client.write_tls()`
|
||||
//! when the underlying connection is able to send data. You should continue doing this
|
||||
//! as long as the connection is valid.
|
||||
//!
|
||||
//! The return types of `read_tls()` and `write_tls()` only tell you if the IO worked. No
|
||||
//! parsing or processing of the TLS messages is done. After each `read_tls()` you should
|
||||
//! therefore call `client.process_new_packets()` which parses and processes the messages.
|
||||
//! Any error returned from `process_new_packets` is fatal to the connection, and will tell you
|
||||
//! why. For example, if the server's certificate is expired `process_new_packets` will
|
||||
//! return `Err(WebPkiError(CertExpired, ValidateServerCert))`. From this point on,
|
||||
//! `process_new_packets` will not do any new work and will return that error continually.
|
||||
//!
|
||||
//! You can extract newly received data by calling `client.reader()` (which implements the
|
||||
//! `io::Read` trait). You can send data to the peer by calling `client.writer()` (which
|
||||
//! implements `io::Write` trait). Note that `client.writer().write()` buffers data you
|
||||
//! send if the TLS connection is not yet established: this is useful for writing (say) a
|
||||
//! HTTP request, but this is buffered so avoid large amounts of data.
|
||||
//!
|
||||
//! The following code uses a fictional socket IO API for illustration, and does not handle
|
||||
//! errors.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! # let mut client = rustls::ClientConnection::new(panic!(), panic!()).unwrap();
|
||||
//! # struct Socket { }
|
||||
//! # impl Socket {
|
||||
//! # fn ready_for_write(&self) -> bool { false }
|
||||
//! # fn ready_for_read(&self) -> bool { false }
|
||||
//! # fn wait_for_something_to_happen(&self) { }
|
||||
//! # }
|
||||
//! #
|
||||
//! # use std::io::{Read, Write, Result};
|
||||
//! # impl Read for Socket {
|
||||
//! # fn read(&mut self, buf: &mut [u8]) -> Result<usize> { panic!() }
|
||||
//! # }
|
||||
//! # impl Write for Socket {
|
||||
//! # fn write(&mut self, buf: &[u8]) -> Result<usize> { panic!() }
|
||||
//! # fn flush(&mut self) -> Result<()> { panic!() }
|
||||
//! # }
|
||||
//! #
|
||||
//! # fn connect(_address: &str, _port: u16) -> Socket {
|
||||
//! # panic!();
|
||||
//! # }
|
||||
//! use std::io;
|
||||
//! use rustls::Connection;
|
||||
//!
|
||||
//! client.writer().write(b"GET / HTTP/1.0\r\n\r\n").unwrap();
|
||||
//! let mut socket = connect("example.com", 443);
|
||||
//! loop {
|
||||
//! if client.wants_read() && socket.ready_for_read() {
|
||||
//! client.read_tls(&mut socket).unwrap();
|
||||
//! client.process_new_packets().unwrap();
|
||||
//!
|
||||
//! let mut plaintext = Vec::new();
|
||||
//! client.reader().read_to_end(&mut plaintext).unwrap();
|
||||
//! io::stdout().write(&plaintext).unwrap();
|
||||
//! }
|
||||
//!
|
||||
//! if client.wants_write() && socket.ready_for_write() {
|
||||
//! client.write_tls(&mut socket).unwrap();
|
||||
//! }
|
||||
//!
|
||||
//! socket.wait_for_something_to_happen();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Examples
|
||||
//! [`tlsserver`](https://github.com/rustls/rustls/blob/main/examples/src/bin/tlsserver-mio.rs)
|
||||
//! and [`tlsclient`](https://github.com/rustls/rustls/blob/main/examples/src/bin/tlsclient-mio.rs)
|
||||
//! are full worked examples. These both use mio.
|
||||
//!
|
||||
//! # Crate features
|
||||
//! Here's a list of what features are exposed by the rustls crate and what
|
||||
//! they mean.
|
||||
//!
|
||||
//! - `logging`: this makes the rustls crate depend on the `log` crate.
|
||||
//! rustls outputs interesting protocol-level messages at `trace!` and `debug!`
|
||||
//! level, and protocol-level errors at `warn!` and `error!` level. The log
|
||||
//! messages do not contain secret key data, and so are safe to archive without
|
||||
//! affecting session security. This feature is in the default set.
|
||||
//!
|
||||
//! - `dangerous_configuration`: this feature enables a `dangerous()` method on
|
||||
//! `ClientConfig` and `ServerConfig` that allows setting inadvisable options,
|
||||
//! such as replacing the certificate verification process. Applications
|
||||
//! requesting this feature should be reviewed carefully.
|
||||
//!
|
||||
//! - `quic`: this feature exposes additional constructors and functions
|
||||
//! for using rustls as a TLS library for QUIC. See the `quic` module for
|
||||
//! details of these. You will only need this if you're writing a QUIC
|
||||
//! implementation.
|
||||
//!
|
||||
//! - `tls12`: enables support for TLS version 1.2. This feature is in the default
|
||||
//! set. Note that, due to the additive nature of Cargo features and because it
|
||||
//! is enabled by default, other crates in your dependency graph could re-enable
|
||||
//! it for your application. If you want to disable TLS 1.2 for security reasons,
|
||||
//! consider explicitly enabling TLS 1.3 only in the config builder API.
|
||||
//!
|
||||
//! - `read_buf`: When building with Rust Nightly, adds support for the unstable
|
||||
//! `std::io::ReadBuf` and related APIs. This reduces costs from initializing
|
||||
//! buffers. Will do nothing on non-Nightly releases.
|
||||
|
||||
// Require docs for public APIs, deny unsafe code, etc.
|
||||
#![forbid(unsafe_code, unused_must_use)]
|
||||
#![cfg_attr(not(read_buf), forbid(unstable_features))]
|
||||
#![deny(
|
||||
clippy::clone_on_ref_ptr,
|
||||
clippy::use_self,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
missing_docs,
|
||||
unreachable_pub,
|
||||
unused_import_braces,
|
||||
unused_extern_crates
|
||||
)]
|
||||
// Relax these clippy lints:
|
||||
// - ptr_arg: this triggers on references to type aliases that are Vec
|
||||
// underneath.
|
||||
// - too_many_arguments: some things just need a lot of state, wrapping it
|
||||
// doesn't necessarily make it easier to follow what's going on
|
||||
// - new_ret_no_self: we sometimes return `Arc<Self>`, which seems fine
|
||||
// - single_component_path_imports: our top-level `use log` import causes
|
||||
// a false positive, https://github.com/rust-lang/rust-clippy/issues/5210
|
||||
// - new_without_default: for internal constructors, the indirection is not
|
||||
// helpful
|
||||
#![allow(
|
||||
clippy::too_many_arguments,
|
||||
clippy::new_ret_no_self,
|
||||
clippy::ptr_arg,
|
||||
clippy::single_component_path_imports,
|
||||
clippy::new_without_default
|
||||
)]
|
||||
// Enable documentation for all features on docs.rs
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
// XXX: Because of https://github.com/rust-lang/rust/issues/54726, we cannot
|
||||
// write `#![rustversion::attr(nightly, feature(read_buf))]` here. Instead,
|
||||
// build.rs set `read_buf` for (only) Rust Nightly to get the same effect.
|
||||
//
|
||||
// All the other conditional logic in the crate could use
|
||||
// `#[rustversion::nightly]` instead of `#[cfg(read_buf)]`; `#[cfg(read_buf)]`
|
||||
// is used to avoid needing `rustversion` to be compiled twice during
|
||||
// cross-compiling.
|
||||
#![cfg_attr(read_buf, feature(read_buf))]
|
||||
|
||||
// log for logging (optional).
|
||||
#[cfg(feature = "logging")]
|
||||
use log;
|
||||
|
||||
#[cfg(not(feature = "logging"))]
|
||||
#[macro_use]
|
||||
mod log {
|
||||
macro_rules! trace ( ($($tt:tt)*) => {{}} );
|
||||
macro_rules! debug ( ($($tt:tt)*) => {{}} );
|
||||
macro_rules! warn ( ($($tt:tt)*) => {{}} );
|
||||
macro_rules! error ( ($($tt:tt)*) => {{}} );
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod msgs;
|
||||
mod anchors;
|
||||
mod cipher;
|
||||
mod conn;
|
||||
mod error;
|
||||
mod hash_hs;
|
||||
mod limited_cache;
|
||||
mod rand;
|
||||
mod record_layer;
|
||||
mod stream;
|
||||
#[cfg(feature = "tls12")]
|
||||
mod tls12;
|
||||
mod tls13;
|
||||
mod vecbuf;
|
||||
mod verify;
|
||||
#[cfg(test)]
|
||||
mod verifybench;
|
||||
mod x509;
|
||||
#[macro_use]
|
||||
mod check;
|
||||
mod bs_debug;
|
||||
mod builder;
|
||||
mod enums;
|
||||
mod key;
|
||||
mod key_log;
|
||||
mod key_log_file;
|
||||
mod kx;
|
||||
mod suites;
|
||||
mod ticketer;
|
||||
mod versions;
|
||||
|
||||
/// Internal classes which may be useful outside the library.
|
||||
/// The contents of this section DO NOT form part of the stable interface.
|
||||
pub mod internal {
|
||||
/// Low-level TLS message parsing and encoding functions.
|
||||
pub mod msgs {
|
||||
pub use crate::msgs::*;
|
||||
}
|
||||
/// Low-level TLS message decryption functions.
|
||||
pub mod cipher {
|
||||
pub use crate::cipher::MessageDecrypter;
|
||||
}
|
||||
}
|
||||
|
||||
// The public interface is:
|
||||
pub use crate::anchors::{OwnedTrustAnchor, RootCertStore};
|
||||
pub use crate::builder::{
|
||||
ConfigBuilder, ConfigSide, WantsCipherSuites, WantsKxGroups, WantsVerifier, WantsVersions,
|
||||
};
|
||||
pub use crate::conn::{
|
||||
CommonState, Connection, ConnectionCommon, IoState, Reader, SideData, Writer,
|
||||
};
|
||||
pub use crate::enums::{CipherSuite, ProtocolVersion, SignatureScheme};
|
||||
pub use crate::error::Error;
|
||||
pub use crate::key::{Certificate, PrivateKey};
|
||||
pub use crate::key_log::{KeyLog, NoKeyLog};
|
||||
pub use crate::key_log_file::KeyLogFile;
|
||||
pub use crate::kx::{SupportedKxGroup, ALL_KX_GROUPS};
|
||||
pub use crate::msgs::enums::{
|
||||
AlertDescription, ContentType, HandshakeType, NamedGroup, SignatureAlgorithm,
|
||||
};
|
||||
pub use crate::msgs::handshake::{DigitallySignedStruct, DistinguishedNames};
|
||||
pub use crate::stream::{Stream, StreamOwned};
|
||||
pub use crate::suites::{
|
||||
BulkAlgorithm, SupportedCipherSuite, ALL_CIPHER_SUITES, DEFAULT_CIPHER_SUITES,
|
||||
};
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub use crate::suites::{ConnectionTrafficSecrets, ExtractedSecrets};
|
||||
pub use crate::ticketer::Ticketer;
|
||||
#[cfg(feature = "tls12")]
|
||||
pub use crate::tls12::Tls12CipherSuite;
|
||||
pub use crate::tls13::Tls13CipherSuite;
|
||||
pub use crate::versions::{SupportedProtocolVersion, ALL_VERSIONS, DEFAULT_VERSIONS};
|
||||
|
||||
/// Items for use in a client.
|
||||
pub mod client {
|
||||
pub(super) mod builder;
|
||||
mod client_conn;
|
||||
mod common;
|
||||
pub(super) mod handy;
|
||||
mod hs;
|
||||
#[cfg(feature = "tls12")]
|
||||
mod tls12;
|
||||
mod tls13;
|
||||
|
||||
pub use builder::{WantsClientCert, WantsTransparencyPolicyOrClientCert};
|
||||
#[cfg(feature = "quic")]
|
||||
pub use client_conn::ClientQuicExt;
|
||||
pub use client_conn::InvalidDnsNameError;
|
||||
pub use client_conn::ResolvesClientCert;
|
||||
pub use client_conn::ServerName;
|
||||
pub use client_conn::StoresClientSessions;
|
||||
pub use client_conn::{
|
||||
ClientConfig, ClientConnection, ClientConnectionData, ClientSessionIdGenerator,
|
||||
ClientSessionIdGenerators, ClientTls12SessionIdGenerator, WriteEarlyData,
|
||||
};
|
||||
pub use handy::{ClientSessionMemoryCache, NoClientSessionStorage};
|
||||
|
||||
#[cfg(feature = "dangerous_configuration")]
|
||||
pub use crate::verify::{
|
||||
CertificateTransparencyPolicy, HandshakeSignatureValid, ServerCertVerified,
|
||||
ServerCertVerifier, WebPkiVerifier,
|
||||
};
|
||||
#[cfg(feature = "dangerous_configuration")]
|
||||
pub use client_conn::danger::DangerousClientConfig;
|
||||
}
|
||||
|
||||
pub use client::{
|
||||
ClientConfig, ClientConnection, ClientSessionIdGenerator, ClientSessionIdGenerators,
|
||||
ClientTls12SessionIdGenerator, ServerName,
|
||||
};
|
||||
|
||||
/// Items for use in a server.
|
||||
pub mod server {
|
||||
pub(crate) mod builder;
|
||||
mod common;
|
||||
pub(crate) mod handy;
|
||||
mod hs;
|
||||
mod server_conn;
|
||||
#[cfg(feature = "tls12")]
|
||||
mod tls12;
|
||||
mod tls13;
|
||||
|
||||
pub use crate::verify::{
|
||||
AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth,
|
||||
};
|
||||
pub use builder::WantsServerCert;
|
||||
pub use handy::ResolvesServerCertUsingSni;
|
||||
pub use handy::{NoServerSessionStorage, ServerSessionMemoryCache};
|
||||
#[cfg(feature = "quic")]
|
||||
pub use server_conn::ServerQuicExt;
|
||||
pub use server_conn::StoresServerSessions;
|
||||
pub use server_conn::{
|
||||
Accepted, Acceptor, ReadEarlyData, ServerConfig, ServerConnection, ServerConnectionData,
|
||||
};
|
||||
pub use server_conn::{ClientHello, ProducesTickets, ResolvesServerCert};
|
||||
|
||||
#[cfg(feature = "dangerous_configuration")]
|
||||
pub use crate::verify::{ClientCertVerified, ClientCertVerifier, DnsName};
|
||||
}
|
||||
|
||||
pub use server::{ServerConfig, ServerConnection};
|
||||
|
||||
/// All defined ciphersuites appear in this module.
|
||||
///
|
||||
/// [`ALL_CIPHER_SUITES`] is provided as an array of all of these values.
|
||||
pub mod cipher_suite {
|
||||
pub use crate::suites::CipherSuiteCommon;
|
||||
#[cfg(feature = "tls12")]
|
||||
pub use crate::tls12::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
|
||||
#[cfg(feature = "tls12")]
|
||||
pub use crate::tls12::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
|
||||
#[cfg(feature = "tls12")]
|
||||
pub use crate::tls12::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
|
||||
#[cfg(feature = "tls12")]
|
||||
pub use crate::tls12::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
|
||||
#[cfg(feature = "tls12")]
|
||||
pub use crate::tls12::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
|
||||
#[cfg(feature = "tls12")]
|
||||
pub use crate::tls12::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
|
||||
pub use crate::tls13::TLS13_AES_128_GCM_SHA256;
|
||||
pub use crate::tls13::TLS13_AES_256_GCM_SHA384;
|
||||
pub use crate::tls13::TLS13_CHACHA20_POLY1305_SHA256;
|
||||
}
|
||||
|
||||
/// All defined protocol versions appear in this module.
|
||||
///
|
||||
/// ALL_VERSIONS is a provided as an array of all of these values.
|
||||
pub mod version {
|
||||
#[cfg(feature = "tls12")]
|
||||
pub use crate::versions::TLS12;
|
||||
pub use crate::versions::TLS13;
|
||||
}
|
||||
|
||||
/// All defined key exchange groups appear in this module.
|
||||
///
|
||||
/// ALL_KX_GROUPS is provided as an array of all of these values.
|
||||
pub mod kx_group {
|
||||
pub use crate::kx::SECP256R1;
|
||||
pub use crate::kx::SECP384R1;
|
||||
pub use crate::kx::X25519;
|
||||
}
|
||||
|
||||
/// Message signing interfaces and implementations.
|
||||
pub mod sign;
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "quic")))]
|
||||
/// APIs for implementing QUIC TLS
|
||||
pub mod quic;
|
||||
|
||||
/// This is the rustls manual.
|
||||
pub mod manual;
|
||||
|
||||
/** Type renames. */
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.20.0", note = "Use ResolvesServerCertUsingSni")]
|
||||
pub type ResolvesServerCertUsingSNI = server::ResolvesServerCertUsingSni;
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[cfg(feature = "dangerous_configuration")]
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.20.0", note = "Use client::WebPkiVerifier")]
|
||||
pub type WebPKIVerifier = client::WebPkiVerifier;
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.20.0", note = "Use Error")]
|
||||
pub type TLSError = Error;
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.20.0", note = "Use ClientConnection")]
|
||||
pub type ClientSession = ClientConnection;
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.20.0", note = "Use ServerConnection")]
|
||||
pub type ServerSession = ServerConnection;
|
||||
|
||||
/* Apologies: would make a trait alias here, but those remain unstable.
|
||||
pub trait Session = Connection;
|
||||
*/
|
||||
175
third_party/rustls-fork-shadow-tls/src/limited_cache.rs
vendored
Normal file
175
third_party/rustls-fork-shadow-tls/src/limited_cache.rs
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::hash::Hash;
|
||||
|
||||
/// A HashMap-alike, which never gets larger than a specified
|
||||
/// capacity, and evicts the oldest insertion to maintain this.
|
||||
///
|
||||
/// The requested capacity may be rounded up by the underlying
|
||||
/// collections. This implementation uses all the allocated
|
||||
/// storage.
|
||||
///
|
||||
/// This is inefficient: it stores keys twice.
|
||||
pub(crate) struct LimitedCache<K: Clone + Hash + Eq, V> {
|
||||
map: HashMap<K, V>,
|
||||
|
||||
// first item is the oldest key
|
||||
oldest: VecDeque<K>,
|
||||
}
|
||||
|
||||
impl<K, V> LimitedCache<K, V>
|
||||
where
|
||||
K: Eq + Hash + Clone + std::fmt::Debug,
|
||||
{
|
||||
/// Create a new LimitedCache with the given rough capacity.
|
||||
pub(crate) fn new(capacity_order_of_magnitude: usize) -> Self {
|
||||
Self {
|
||||
map: HashMap::with_capacity(capacity_order_of_magnitude),
|
||||
oldest: VecDeque::with_capacity(capacity_order_of_magnitude),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&mut self, k: K, v: V) {
|
||||
let inserted_new_item = match self.map.entry(k) {
|
||||
Entry::Occupied(mut old) => {
|
||||
// nb. does not freshen entry in `oldest`
|
||||
old.insert(v);
|
||||
false
|
||||
}
|
||||
|
||||
entry @ Entry::Vacant(_) => {
|
||||
self.oldest
|
||||
.push_back(entry.key().clone());
|
||||
entry.or_insert(v);
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
// ensure next insert() does not require a realloc
|
||||
if inserted_new_item && self.oldest.capacity() == self.oldest.len() {
|
||||
if let Some(oldest_key) = self.oldest.pop_front() {
|
||||
self.map.remove(&oldest_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
self.map.get(k)
|
||||
}
|
||||
|
||||
pub(crate) fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
if let Some(value) = self.map.remove(k) {
|
||||
// O(N) search, followed by O(N) removal
|
||||
if let Some(index) = self
|
||||
.oldest
|
||||
.iter()
|
||||
.position(|item| item.borrow() == k)
|
||||
{
|
||||
self.oldest.remove(index);
|
||||
}
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
type Test = super::LimitedCache<String, usize>;
|
||||
|
||||
#[test]
|
||||
fn test_updates_existing_item() {
|
||||
let mut t = Test::new(3);
|
||||
t.insert("abc".into(), 1);
|
||||
t.insert("abc".into(), 2);
|
||||
assert_eq!(t.get("abc"), Some(&2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_evicts_oldest_item() {
|
||||
let mut t = Test::new(3);
|
||||
t.insert("abc".into(), 1);
|
||||
t.insert("def".into(), 2);
|
||||
t.insert("ghi".into(), 3);
|
||||
|
||||
assert_eq!(t.get("abc"), None);
|
||||
assert_eq!(t.get("def"), Some(&2));
|
||||
assert_eq!(t.get("ghi"), Some(&3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_evicts_second_oldest_item_if_first_removed() {
|
||||
let mut t = Test::new(3);
|
||||
t.insert("abc".into(), 1);
|
||||
t.insert("def".into(), 2);
|
||||
|
||||
assert_eq!(t.remove("abc"), Some(1));
|
||||
|
||||
t.insert("ghi".into(), 3);
|
||||
t.insert("jkl".into(), 4);
|
||||
|
||||
assert_eq!(t.get("abc"), None);
|
||||
assert_eq!(t.get("def"), None);
|
||||
assert_eq!(t.get("ghi"), Some(&3));
|
||||
assert_eq!(t.get("jkl"), Some(&4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_evicts_after_second_oldest_item_removed() {
|
||||
let mut t = Test::new(3);
|
||||
t.insert("abc".into(), 1);
|
||||
t.insert("def".into(), 2);
|
||||
|
||||
assert_eq!(t.remove("def"), Some(2));
|
||||
assert_eq!(t.get("abc"), Some(&1));
|
||||
|
||||
t.insert("ghi".into(), 3);
|
||||
t.insert("jkl".into(), 4);
|
||||
|
||||
assert_eq!(t.get("abc"), None);
|
||||
assert_eq!(t.get("def"), None);
|
||||
assert_eq!(t.get("ghi"), Some(&3));
|
||||
assert_eq!(t.get("jkl"), Some(&4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_removes_all_items() {
|
||||
let mut t = Test::new(3);
|
||||
t.insert("abc".into(), 1);
|
||||
t.insert("def".into(), 2);
|
||||
|
||||
assert_eq!(t.remove("def"), Some(2));
|
||||
assert_eq!(t.remove("abc"), Some(1));
|
||||
|
||||
t.insert("ghi".into(), 3);
|
||||
t.insert("jkl".into(), 4);
|
||||
t.insert("mno".into(), 5);
|
||||
|
||||
assert_eq!(t.get("abc"), None);
|
||||
assert_eq!(t.get("def"), None);
|
||||
assert_eq!(t.get("ghi"), None);
|
||||
assert_eq!(t.get("jkl"), Some(&4));
|
||||
assert_eq!(t.get("mno"), Some(&5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inserts_many_items() {
|
||||
let mut t = Test::new(3);
|
||||
|
||||
for _ in 0..10000 {
|
||||
t.insert("abc".into(), 1);
|
||||
t.insert("def".into(), 2);
|
||||
t.insert("ghi".into(), 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
third_party/rustls-fork-shadow-tls/src/manual/defaults.rs
vendored
Normal file
29
third_party/rustls-fork-shadow-tls/src/manual/defaults.rs
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*!
|
||||
|
||||
## Rationale for defaults
|
||||
|
||||
### Why is AES-256 preferred over AES-128?
|
||||
|
||||
This is a trade-off between:
|
||||
|
||||
1. classical security level: searching a 2^128 key space is as implausible as 2^256.
|
||||
2. post-quantum security level: the difference is more meaningful, and AES-256 seems like the conservative choice.
|
||||
3. performance: AES-256 is around 40% slower than AES-128, though hardware acceleration typically narrows this gap.
|
||||
|
||||
The choice is frankly quite marginal.
|
||||
|
||||
### Why is AES-GCM preferred over chacha20-poly1305?
|
||||
|
||||
Hardware support for accelerating AES-GCM is widespread, and hardware-accelerated AES-GCM
|
||||
is quicker than un-accelerated chacha20-poly1305.
|
||||
|
||||
However, if you know your application will run on a platform without that, you should
|
||||
_definitely_ change the default order to prefer chacha20-poly1305: both the performance and
|
||||
the implementation security will be improved. We think this is an uncommon case.
|
||||
|
||||
### Why is x25519 preferred for key exchange over nistp256?
|
||||
|
||||
Both provide roughly the same classical security level, but x25519 has better performance and
|
||||
it's _much_ more likely that both peers will have good quality implementations.
|
||||
|
||||
*/
|
||||
50
third_party/rustls-fork-shadow-tls/src/manual/features.rs
vendored
Normal file
50
third_party/rustls-fork-shadow-tls/src/manual/features.rs
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*!
|
||||
|
||||
## Current features
|
||||
|
||||
* TLS1.2 and TLS1.3.
|
||||
* ECDSA, Ed25519 or RSA server authentication by clients.
|
||||
* ECDSA, Ed25519 or RSA server authentication by servers.
|
||||
* Forward secrecy using ECDHE; with curve25519, nistp256 or nistp384 curves.
|
||||
* AES128-GCM and AES256-GCM bulk encryption, with safe nonces.
|
||||
* ChaCha20-Poly1305 bulk encryption ([RFC7905](https://tools.ietf.org/html/rfc7905)).
|
||||
* ALPN support.
|
||||
* SNI support.
|
||||
* Tunable MTU to make TLS messages match size of underlying transport.
|
||||
* Optional use of vectored IO to minimise system calls.
|
||||
* TLS1.2 session resumption.
|
||||
* TLS1.2 resumption via tickets (RFC5077).
|
||||
* TLS1.3 resumption via tickets or session storage.
|
||||
* TLS1.3 0-RTT data for clients.
|
||||
* Client authentication by clients.
|
||||
* Client authentication by servers.
|
||||
* Extended master secret support (RFC7627).
|
||||
* Exporters (RFC5705).
|
||||
* OCSP stapling by servers.
|
||||
* SCT stapling by servers.
|
||||
* SCT verification by clients.
|
||||
|
||||
## Possible future features
|
||||
|
||||
* PSK support.
|
||||
* OCSP verification by clients.
|
||||
* Certificate pinning.
|
||||
|
||||
## Non-features
|
||||
|
||||
For reasons explained in the other sections of this manual, rustls does not
|
||||
and will not support:
|
||||
|
||||
* SSL1, SSL2, SSL3, TLS1 or TLS1.1.
|
||||
* RC4.
|
||||
* DES or triple DES.
|
||||
* EXPORT ciphersuites.
|
||||
* MAC-then-encrypt ciphersuites.
|
||||
* Ciphersuites without forward secrecy.
|
||||
* Renegotiation.
|
||||
* Kerberos.
|
||||
* Compression.
|
||||
* Discrete-log Diffie-Hellman.
|
||||
* Automatic protocol version downgrade.
|
||||
|
||||
*/
|
||||
36
third_party/rustls-fork-shadow-tls/src/manual/howto.rs
vendored
Normal file
36
third_party/rustls-fork-shadow-tls/src/manual/howto.rs
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*! # Customising private key usage
|
||||
|
||||
By default rustls supports PKCS#8-format[^1] RSA or ECDSA keys, plus PKCS#1-format RSA keys.
|
||||
|
||||
However, if your private key resides in a HSM, or in another process, or perhaps
|
||||
another machine, rustls has some extension points to support this:
|
||||
|
||||
The main trait you must implement is [`sign::SigningKey`][signing_key]. The primary method here
|
||||
is [`choose_scheme`][choose_scheme] where you are given a set of [`SignatureScheme`s][sig_scheme] the client says
|
||||
it supports: you must choose one (or return `None` -- this aborts the handshake). Having
|
||||
done that, you return an implementation of the [`sign::Signer`][signer] trait.
|
||||
The [`sign()`][sign_method] performs the signature and returns it.
|
||||
|
||||
(Unfortunately this is currently designed for keys with low latency access, like in a
|
||||
PKCS#11 provider, Microsoft CryptoAPI, etc. so is blocking rather than asynchronous.
|
||||
It's a TODO to make these and other extension points async.)
|
||||
|
||||
Once you have these two pieces, configuring a server to use them involves, briefly:
|
||||
|
||||
- packaging your `sign::SigningKey` with the matching certificate chain into a [`sign::CertifiedKey`][certified_key]
|
||||
- making a [`ResolvesServerCertUsingSni`][cert_using_sni] and feeding in your `sign::CertifiedKey` for all SNI hostnames you want to use it for,
|
||||
- setting that as your `ServerConfig`'s [`cert_resolver`][cert_resolver]
|
||||
|
||||
[signing_key]: ../../sign/trait.SigningKey.html
|
||||
[choose_scheme]: ../../sign/trait.SigningKey.html#tymethod.choose_scheme
|
||||
[sig_scheme]: ../../enum.SignatureScheme.html
|
||||
[signer]: ../../sign/trait.Signer.html
|
||||
[sign_method]: ../../sign/trait.Signer.html#tymethod.sign
|
||||
[certified_key]: ../../sign/struct.CertifiedKey.html
|
||||
[cert_using_sni]: ../../struct.ResolvesServerCertUsingSni.html
|
||||
[cert_resolver]: ../../struct.ServerConfig.html#structfield.cert_resolver
|
||||
|
||||
[^1]: For PKCS#8 it does not support password encryption -- there's not a meaningful threat
|
||||
model addressed by this, and the encryption supported is typically extremely poor.
|
||||
|
||||
*/
|
||||
104
third_party/rustls-fork-shadow-tls/src/manual/implvulns.rs
vendored
Normal file
104
third_party/rustls-fork-shadow-tls/src/manual/implvulns.rs
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*! # A review of TLS Implementation Vulnerabilities
|
||||
|
||||
An important part of engineering involves studying and learning from the mistakes of the past.
|
||||
It would be tremendously unfortunate to spend effort re-discovering and re-fixing the same
|
||||
vulnerabilities that were discovered in the past.
|
||||
|
||||
## Memory safety
|
||||
|
||||
Being written entirely in the safe-subset of Rust immediately offers us freedom from the entire
|
||||
class of memory safety vulnerabilities. There are too many to exhaustively list, and there will
|
||||
certainly be more in the future.
|
||||
|
||||
Examples:
|
||||
|
||||
- Heartbleed [CVE-2014-0160](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160) (OpenSSL)
|
||||
- Memory corruption in ASN.1 decoder [CVE-2016-2108](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2108) (OpenSSL)
|
||||
- Buffer overflow in read_server_hello [CVE-2014-3466](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3466) (GnuTLS)
|
||||
|
||||
## `goto fail`
|
||||
|
||||
This is the name of a vulnerability in Apple Secure Transport [CVE-2014-1266](https://nvd.nist.gov/vuln/detail/CVE-2014-1266).
|
||||
This boiled down to the following code, which validates the server's signature on the key exchange:
|
||||
|
||||
```c
|
||||
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
|
||||
goto fail;
|
||||
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
|
||||
goto fail;
|
||||
> goto fail;
|
||||
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
|
||||
goto fail;
|
||||
```
|
||||
|
||||
The marked line was duplicated, likely accidentally during a merge. This meant
|
||||
the remaining part of the function (including the actual signature validation)
|
||||
was unconditionally skipped.
|
||||
|
||||
Ultimately the one countermeasure to this type of bug is basic testing: that a
|
||||
valid signature returns success, and that an invalid one does not. rustls
|
||||
has such testing, but this is really table stakes for security code.
|
||||
|
||||
Further than this, though, we could consider that the *lack* of an error from
|
||||
this function is a poor indicator that the signature was valid. rustls, instead,
|
||||
has zero-size and non-copyable types that indicate a particular signature validation
|
||||
has been performed. These types can be thought of as *capabilities* originated only
|
||||
by designated signature verification functions -- such functions can then be a focus
|
||||
of manual code review. Like capabilities, values of these types are otherwise unforgeable,
|
||||
and are communicable only by Rust's move semantics.
|
||||
|
||||
Values of these types are threaded through the protocol state machine, leading to terminal
|
||||
states that look like:
|
||||
|
||||
```ignore
|
||||
struct ExpectTraffic {
|
||||
(...)
|
||||
_cert_verified: verify::ServerCertVerified,
|
||||
_sig_verified: verify::HandshakeSignatureValid,
|
||||
_fin_verified: verify::FinishedMessageVerified,
|
||||
}
|
||||
```
|
||||
|
||||
Since this state requires a value of these types, it will be a compile-time error to
|
||||
reach that state without performing the requisite security-critical operations.
|
||||
|
||||
This approach is not infallible, but it has zero runtime cost.
|
||||
|
||||
## State machine attacks: EarlyCCS and SMACK/SKIP/FREAK
|
||||
|
||||
EarlyCCS [CVE-2014-0224](https://nvd.nist.gov/vuln/detail/CVE-2014-0224) was a vulnerability in OpenSSL
|
||||
found in 2014. The TLS `ChangeCipherSpec` message would be processed at inappropriate times, leading
|
||||
to data being encrypted with the wrong keys (specifically, keys which were not secret). This resulted
|
||||
from OpenSSL taking a *reactive* strategy to incoming messages ("when I get a message X, I should do Y")
|
||||
which allows it to diverge from the proper state machine under attacker control.
|
||||
|
||||
[SMACK](https://mitls.org/pages/attacks/SMACK) is a similar suite of vulnerabilities found in JSSE,
|
||||
CyaSSL, OpenSSL, Mono and axTLS. "SKIP-TLS" demonstrated that some implementations allowed handshake
|
||||
messages (and in one case, the entire handshake!) to be skipped leading to breaks in security. "FREAK"
|
||||
found that some implementations incorrectly allowed export-only state transitions (i.e., transitions that
|
||||
were only valid when an export ciphersuite was in use).
|
||||
|
||||
rustls represents its protocol state machine carefully to avoid these defects. We model the handshake,
|
||||
CCS and application data subprotocols in the same single state machine. Each state in this machine is
|
||||
represented with a single struct, and transitions are modelled as functions that consume the current state
|
||||
plus one TLS message[^1] and return a struct representing the next state. These functions fully validate
|
||||
the message type before further operations.
|
||||
|
||||
A sample sequence for a full TLSv1.2 handshake by a client looks like:
|
||||
|
||||
- `hs::ExpectServerHello` (nb. ClientHello is logically sent before this state); transition to `tls12::ExpectCertificate`
|
||||
- `tls12::ExpectCertificate`; transition to `tls12::ExpectServerKX`
|
||||
- `tls12::ExpectServerKX`; transition to `tls12::ExpectServerDoneOrCertReq`
|
||||
- `tls12::ExpectServerDoneOrCertReq`; delegates to `tls12::ExpectCertificateRequest` or `tls12::ExpectServerDone` depending on incoming message.
|
||||
- `tls12::ExpectServerDone`; transition to `tls12::ExpectCCS`
|
||||
- `tls12::ExpectCCS`; transition to `tls12::ExpectFinished`
|
||||
- `tls12::ExpectFinished`; transition to `tls12::ExpectTraffic`
|
||||
- `tls12::ExpectTraffic`; terminal state; transitions to `tls12::ExpectTraffic`
|
||||
|
||||
In the future we plan to formally prove that all possible transitions modelled in this system of types
|
||||
are correct with respect to the standard(s). At the moment we rely merely on exhaustive testing.
|
||||
|
||||
[^1]: a logical TLS message: post-decryption, post-fragmentation.
|
||||
|
||||
|
||||
*/
|
||||
30
third_party/rustls-fork-shadow-tls/src/manual/mod.rs
vendored
Normal file
30
third_party/rustls-fork-shadow-tls/src/manual/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*!
|
||||
|
||||
This documentation primarily aims to explain design decisions taken in rustls.
|
||||
|
||||
It does this from a few aspects: how rustls attempts to avoid construction errors
|
||||
that occurred in other TLS libraries, how rustls attempts to avoid past TLS
|
||||
protocol vulnerabilities, and assorted advice for achieving common tasks with rustls.
|
||||
*/
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
/// This section discusses vulnerabilities in other TLS implementations, theorising their
|
||||
/// root cause and how we aim to avoid them in rustls.
|
||||
#[path = "implvulns.rs"]
|
||||
pub mod _01_impl_vulnerabilities;
|
||||
|
||||
/// This section discusses vulnerabilities and design errors in the TLS protocol.
|
||||
#[path = "tlsvulns.rs"]
|
||||
pub mod _02_tls_vulnerabilities;
|
||||
|
||||
/// This section collects together goal-oriented documentation.
|
||||
#[path = "howto.rs"]
|
||||
pub mod _03_howto;
|
||||
|
||||
/// This section documents rustls itself: what protocol features are and are not implemented.
|
||||
#[path = "features.rs"]
|
||||
pub mod _04_features;
|
||||
|
||||
/// This section provides rationale for the defaults in rustls.
|
||||
#[path = "defaults.rs"]
|
||||
pub mod _05_defaults;
|
||||
173
third_party/rustls-fork-shadow-tls/src/manual/tlsvulns.rs
vendored
Normal file
173
third_party/rustls-fork-shadow-tls/src/manual/tlsvulns.rs
vendored
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*! # A review of protocol vulnerabilities
|
||||
|
||||
## CBC MAC-then-encrypt ciphersuites
|
||||
|
||||
Back in 2000 [Bellare and Namprempre](https://eprint.iacr.org/2000/025) discussed how to make authenticated
|
||||
encryption by composing separate encryption and authentication primitives. That paper included this table:
|
||||
|
||||
| Composition Method | Privacy || Integrity ||
|
||||
|--------------------|---------||-----------||
|
||||
|| IND-CPA | IND-CCA | NM-CPA | INT-PTXT | INT-CTXT |
|
||||
| Encrypt-and-MAC | insecure | insecure | insecure | secure | insecure |
|
||||
| MAC-then-encrypt | secure | insecure | insecure | secure | insecure |
|
||||
| Encrypt-then-MAC | secure | secure | secure | secure | secure |
|
||||
|
||||
One may assume from this fairly clear result that encrypt-and-MAC and MAC-then-encrypt compositions would be quickly abandoned
|
||||
in favour of the remaining proven-secure option. But that didn't happen, not in TLSv1.1 (2006) nor in TLSv1.2 (2008). Worse,
|
||||
both RFCs included incorrect advice on countermeasures for implementers, suggesting that the flaw was "not believed to be large
|
||||
enough to be exploitable".
|
||||
|
||||
[Lucky 13](http://www.isg.rhul.ac.uk/tls/Lucky13.html) (2013) exploited this flaw and affected all implementations, including
|
||||
those written [after discovery](https://aws.amazon.com/blogs/security/s2n-and-lucky-13/). OpenSSL even had a
|
||||
[memory safety vulnerability in the fix for Lucky 13](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2107), which
|
||||
gives a flavour of the kind of complexity required to remove the side channel.
|
||||
|
||||
rustls does not implement CBC MAC-then-encrypt ciphersuites for these reasons. TLSv1.3 removed support for these
|
||||
ciphersuites in 2018.
|
||||
|
||||
There are some further rejected options worth mentioning: [RFC7366](https://tools.ietf.org/html/rfc7366) defines
|
||||
Encrypt-then-MAC for TLS, but unfortunately cannot be negotiated without also supporting MAC-then-encrypt
|
||||
(clients cannot express "I offer CBC, but only EtM and not MtE").
|
||||
|
||||
## RSA PKCS#1 encryption
|
||||
|
||||
"RSA key exchange" in TLS involves the client choosing a large random value and encrypting it using the server's
|
||||
public key. This has two overall problems:
|
||||
|
||||
1. It provides no _forward secrecy_: later compromise of the server's private key breaks confidentiality of
|
||||
*all* past sessions using that key. This is a crucial property in the presence of software that is often
|
||||
[poor at keeping a secret](http://heartbleed.com/).
|
||||
2. The padding used in practice in TLS ("PKCS#1", or fully "RSAES-PKCS1-v1_5") has been known to be broken since
|
||||
[1998](http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf).
|
||||
|
||||
In a similar pattern to the MAC-then-encrypt problem discussed above, TLSv1.0 (1999), TLSv1.1 (2006) and TLSv1.2 (2008)
|
||||
continued to specify use of PKCS#1 encryption, again with incrementally more complex and incorrect advice on countermeasures.
|
||||
|
||||
[ROBOT](https://robotattack.org/) (2018) showed that implementations were still vulnerable to these attacks twenty years later.
|
||||
|
||||
rustls does not support RSA key exchange. TLSv1.3 also removed support.
|
||||
|
||||
## BEAST
|
||||
|
||||
[BEAST](https://vnhacker.blogspot.com/2011/09/beast.html) ([CVE-2011-3389](https://nvd.nist.gov/vuln/detail/CVE-2011-3389))
|
||||
was demonstrated in 2011 by Thai Duong and Juliano Rizzo,
|
||||
and was another vulnerability in CBC-based ciphersuites in SSLv3.0 and TLSv1.0. CBC mode is vulnerable to adaptive
|
||||
chosen-plaintext attacks if the IV is predictable. In the case of these protocol versions, the IV was the previous
|
||||
block of ciphertext (as if the entire TLS session was one CBC ciphertext, albeit revealed incrementally). This was
|
||||
obviously predictable, since it was published on the wire.
|
||||
|
||||
OpenSSL contained a countermeasure for this problem from 2002 onwards: it encrypts an empty message before each real
|
||||
one, so that the IV used in the real message is unpredictable. This was turned off by default due to bugs in IE6.
|
||||
|
||||
TLSv1.1 fix this vulnerability, but not any of the other deficiencies of CBC mode (see above).
|
||||
|
||||
rustls does not support these ciphersuites.
|
||||
|
||||
## CRIME
|
||||
|
||||
In 2002 [John Kelsey](https://www.iacr.org/cryptodb/archive/2002/FSE/3091/3091.pdf) discussed the length side channel
|
||||
as applied to compression of combined secret and attacker-chosen strings.
|
||||
|
||||
Compression continued to be an option in TLSv1.1 (2006) and in TLSv1.2 (2008). Support in libraries was widespread.
|
||||
|
||||
[CRIME](http://netifera.com/research/crime/CRIME_ekoparty2012.pdf) ([CVE-2012-4929](https://nvd.nist.gov/vuln/detail/CVE-2012-4929))
|
||||
was demonstrated in 2012, again by Thai Duong and Juliano Rizzo. It attacked several protocols offering transparent
|
||||
compression of application data, allowing quick adaptive chosen-plaintext attacks against secret values like cookies.
|
||||
|
||||
rustls does not implement compression. TLSv1.3 also removed support.
|
||||
|
||||
## Logjam / FREAK
|
||||
|
||||
Way back when SSL was first being born, circa 1995, the US government considered cryptography a munition requiring
|
||||
export control. SSL contained specific ciphersuites with dramatically small key sizes that were not subject
|
||||
to export control. These controls were dropped in 2000.
|
||||
|
||||
Since the "export-grade" ciphersuites no longer fulfilled any purpose, and because they were actively harmful to users,
|
||||
one may have expected software support to disappear quickly. This did not happen.
|
||||
|
||||
In 2015 [the FREAK attack](https://mitls.org/pages/attacks/SMACK#freak) ([CVE-2015-0204](https://nvd.nist.gov/vuln/detail/CVE-2015-0204))
|
||||
and [the Logjam attack](https://weakdh.org/) ([CVE-2015-4000](https://nvd.nist.gov/vuln/detail/CVE-2015-4000)) both
|
||||
demonstrated total breaks of security in the presence of servers that accepted export ciphersuites. FREAK factored
|
||||
512-bit RSA keys, while Logjam optimised solving discrete logs in the 512-bit group used by many different servers.
|
||||
|
||||
Naturally, rustls does not implement any of these ciphersuites.
|
||||
|
||||
## SWEET32
|
||||
|
||||
Block ciphers are vulnerable to birthday attacks, where the probability of repeating a block increases dramatically
|
||||
once a particular key has been used for many blocks. For block ciphers with 64-bit blocks, this becomes probable
|
||||
once a given key encrypts the order of 32GB of data.
|
||||
|
||||
[Sweet32](https://sweet32.info/) ([CVE-2016-2183](https://nvd.nist.gov/vuln/detail/CVE-2016-2183)) attacked this fact
|
||||
in the context of TLS support for 3DES, breaking confidentiality by analysing a large amount of attacker-induced traffic
|
||||
in one session.
|
||||
|
||||
rustls does not support any 64-bit block ciphers.
|
||||
|
||||
## DROWN
|
||||
|
||||
[DROWN](https://drownattack.com/) ([CVE-2016-0800](https://nvd.nist.gov/vuln/detail/CVE-2016-0800)) is a cross-protocol
|
||||
attack that breaks the security of TLSv1.2 and earlier (when used with RSA key exchange) by using SSLv2. It is required
|
||||
that the server uses the same key for both protocol versions.
|
||||
|
||||
rustls naturally does not support SSLv2, but most importantly does not support RSA key exchange for TLSv1.2.
|
||||
|
||||
## Poodle
|
||||
|
||||
[POODLE](https://www.openssl.org/~bodo/ssl-poodle.pdf) ([CVE-2014-3566](https://nvd.nist.gov/vuln/detail/CVE-2014-3566))
|
||||
is an attack against CBC mode ciphersuites in SSLv3. This was possible in most cases because some clients willingly
|
||||
downgraded to SSLv3 after failed handshakes for later versions.
|
||||
|
||||
rustls does not support CBC mode ciphersuites, or SSLv3. Note that rustls does not need to implement `TLS_FALLBACK_SCSV`
|
||||
introduced as a countermeasure because it contains no ability to downgrade to earlier protocol versions.
|
||||
|
||||
## GCM nonces
|
||||
|
||||
[RFC5288](https://tools.ietf.org/html/rfc5288) introduced GCM-based ciphersuites for use in TLS. Unfortunately
|
||||
the design was poor; it reused design for an unrelated security setting proposed in RFC5116.
|
||||
|
||||
GCM is a typical nonce-based AEAD: it requires a unique (but not necessarily unpredictable) 96-bit nonce for each encryption
|
||||
with a given key. The design specified by RFC5288 left two-thirds of the nonce construction up to implementations:
|
||||
|
||||
- wasting 8 bytes per TLS ciphertext,
|
||||
- meaning correct operation cannot be tested for (e.g., in protocol-level test vectors).
|
||||
|
||||
There were no trade-offs here: TLS has a 64-bit sequence number that is not allowed to wrap and would make an ideal nonce.
|
||||
|
||||
As a result, a [2016 study](https://eprint.iacr.org/2016/475.pdf) found:
|
||||
|
||||
- implementations from IBM, A10 and Citrix used randomly-chosen nonces, which are unlikely to be unique over long connections,
|
||||
- an implementation from Radware used the same nonce for the first two messages.
|
||||
|
||||
rustls uses a counter from a random starting point for GCM nonces. TLSv1.3 and the Chacha20-Poly1305 TLSv1.2 ciphersuite
|
||||
standardise this method.
|
||||
|
||||
## Renegotiation
|
||||
|
||||
In 2009 Marsh Ray and Steve Dispensa [discovered](https://kryptera.se/Renegotiating%20TLS.pdf) that the renegotiation
|
||||
feature of all versions of TLS allows a MitM to splice a request of their choice onto the front of the client's real HTTP
|
||||
request. A countermeasure was proposed and widely implemented to bind renegotiations to their previous negotiations;
|
||||
unfortunately this was insufficient.
|
||||
|
||||
rustls does not support renegotiation in TLSv1.2. TLSv1.3 also no longer supports renegotiation.
|
||||
|
||||
## 3SHAKE
|
||||
|
||||
[3SHAKE](https://www.mitls.org/pages/attacks/3SHAKE) (2014) described a complex attack that broke the "Secure Renegotiation" extension
|
||||
introduced as a countermeasure to the previous protocol flaw.
|
||||
|
||||
rustls does not support renegotiation for TLSv1.2 connections, or RSA key exchange, and both are required for this attack
|
||||
to work. rustls implements the "Extended Master Secret" (RFC7627) extension for TLSv1.2 which was standardised as a countermeasure.
|
||||
|
||||
TLSv1.3 no longer supports renegotiation and RSA key exchange. It also effectively incorporates the improvements made in RFC7627.
|
||||
|
||||
## KCI
|
||||
|
||||
[This vulnerability](https://kcitls.org/) makes use of TLS ciphersuites (those offering static DH) which were standardised
|
||||
yet not widely used. However, they were implemented by libraries, and as a result enabled for various clients. It coupled
|
||||
this with misconfigured certificates (on services including facebook.com) which allowed their misuse to MitM connections.
|
||||
|
||||
rustls does not support static DH/EC-DH ciphersuites. We assert that it is misissuance to sign an EC certificate
|
||||
with the keyUsage extension allowing both signatures and key exchange. That it isn't is probably a failure
|
||||
of CAB Forum baseline requirements.
|
||||
*/
|
||||
22
third_party/rustls-fork-shadow-tls/src/msgs/alert.rs
vendored
Normal file
22
third_party/rustls-fork-shadow-tls/src/msgs/alert.rs
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use crate::msgs::codec::{Codec, Reader};
|
||||
use crate::msgs::enums::{AlertDescription, AlertLevel};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AlertMessagePayload {
|
||||
pub level: AlertLevel,
|
||||
pub description: AlertDescription,
|
||||
}
|
||||
|
||||
impl Codec for AlertMessagePayload {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
self.level.encode(bytes);
|
||||
self.description.encode(bytes);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
let level = AlertLevel::read(r)?;
|
||||
let description = AlertDescription::read(r)?;
|
||||
|
||||
Some(Self { level, description })
|
||||
}
|
||||
}
|
||||
170
third_party/rustls-fork-shadow-tls/src/msgs/base.rs
vendored
Normal file
170
third_party/rustls-fork-shadow-tls/src/msgs/base.rs
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
use std::fmt;
|
||||
|
||||
use crate::key;
|
||||
use crate::msgs::codec;
|
||||
use crate::msgs::codec::{Codec, Reader};
|
||||
|
||||
/// An externally length'd payload
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Payload(pub Vec<u8>);
|
||||
|
||||
impl Codec for Payload {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.extend_from_slice(&self.0);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
Some(Self::read(r))
|
||||
}
|
||||
}
|
||||
|
||||
impl Payload {
|
||||
pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
|
||||
Self(bytes.into())
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self::new(Vec::new())
|
||||
}
|
||||
|
||||
pub fn read(r: &mut Reader) -> Self {
|
||||
Self(r.rest().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl Codec for key::Certificate {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
codec::u24(self.0.len() as u32).encode(bytes);
|
||||
bytes.extend_from_slice(&self.0);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
let len = codec::u24::read(r)?.0 as usize;
|
||||
let mut sub = r.sub(len)?;
|
||||
let body = sub.rest().to_vec();
|
||||
Some(Self(body))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Payload {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
hex(f, &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary, unknown-content, u24-length-prefixed payload
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct PayloadU24(pub Vec<u8>);
|
||||
|
||||
impl PayloadU24 {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl Codec for PayloadU24 {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
codec::u24(self.0.len() as u32).encode(bytes);
|
||||
bytes.extend_from_slice(&self.0);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
let len = codec::u24::read(r)?.0 as usize;
|
||||
let mut sub = r.sub(len)?;
|
||||
let body = sub.rest().to_vec();
|
||||
Some(Self(body))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PayloadU24 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
hex(f, &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary, unknown-content, u16-length-prefixed payload
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct PayloadU16(pub Vec<u8>);
|
||||
|
||||
impl PayloadU16 {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self::new(Vec::new())
|
||||
}
|
||||
|
||||
pub fn encode_slice(slice: &[u8], bytes: &mut Vec<u8>) {
|
||||
(slice.len() as u16).encode(bytes);
|
||||
bytes.extend_from_slice(slice);
|
||||
}
|
||||
}
|
||||
|
||||
impl Codec for PayloadU16 {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
Self::encode_slice(&self.0, bytes);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
let len = u16::read(r)? as usize;
|
||||
let mut sub = r.sub(len)?;
|
||||
let body = sub.rest().to_vec();
|
||||
Some(Self(body))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PayloadU16 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
hex(f, &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary, unknown-content, u8-length-prefixed payload
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct PayloadU8(pub Vec<u8>);
|
||||
|
||||
impl PayloadU8 {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> Vec<u8> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Codec for PayloadU8 {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
(self.0.len() as u8).encode(bytes);
|
||||
bytes.extend_from_slice(&self.0);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
let len = u8::read(r)? as usize;
|
||||
let mut sub = r.sub(len)?;
|
||||
let body = sub.rest().to_vec();
|
||||
Some(Self(body))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PayloadU8 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
hex(f, &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Format an iterator of u8 into a hex string
|
||||
pub(super) fn hex<'a>(
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
payload: impl IntoIterator<Item = &'a u8>,
|
||||
) -> fmt::Result {
|
||||
for b in payload {
|
||||
write!(f, "{:02x}", b)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
20
third_party/rustls-fork-shadow-tls/src/msgs/ccs.rs
vendored
Normal file
20
third_party/rustls-fork-shadow-tls/src/msgs/ccs.rs
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use crate::msgs::codec::{Codec, Reader};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChangeCipherSpecPayload;
|
||||
|
||||
impl Codec for ChangeCipherSpecPayload {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
1u8.encode(bytes);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
let typ = u8::read(r)?;
|
||||
|
||||
if typ == 1 && !r.any_left() {
|
||||
Some(Self {})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
290
third_party/rustls-fork-shadow-tls/src/msgs/codec.rs
vendored
Normal file
290
third_party/rustls-fork-shadow-tls/src/msgs/codec.rs
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
use std::convert::TryInto;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Wrapper over a slice of bytes that allows reading chunks from
|
||||
/// with the current position state held using a cursor.
|
||||
///
|
||||
/// A new reader for a sub section of the the buffer can be created
|
||||
/// using the `sub` function or a section of a certain length can
|
||||
/// be obtained using the `take` function
|
||||
pub struct Reader<'a> {
|
||||
/// The underlying buffer storing the readers content
|
||||
buffer: &'a [u8],
|
||||
/// Stores the current reading position for the buffer
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
impl<'a> Reader<'a> {
|
||||
/// Creates a new Reader of the provided `bytes` slice with
|
||||
/// the initial cursor position of zero.
|
||||
pub fn init(bytes: &[u8]) -> Reader {
|
||||
Reader {
|
||||
buffer: bytes,
|
||||
cursor: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to create a new Reader on a sub section of this
|
||||
/// readers bytes by taking a slice of the provided `length`
|
||||
/// will return None if there is not enough bytes
|
||||
pub fn sub(&mut self, length: usize) -> Option<Reader> {
|
||||
self.take(length).map(Reader::init)
|
||||
}
|
||||
|
||||
/// Borrows a slice of all the remaining bytes
|
||||
/// that appear after the cursor position.
|
||||
///
|
||||
/// Moves the cursor to the end of the buffer length.
|
||||
pub fn rest(&mut self) -> &[u8] {
|
||||
let rest = &self.buffer[self.cursor..];
|
||||
self.cursor = self.buffer.len();
|
||||
rest
|
||||
}
|
||||
|
||||
/// Attempts to borrow a slice of bytes from the current
|
||||
/// cursor position of `length` if there is not enough
|
||||
/// bytes remaining after the cursor to take the length
|
||||
/// then None is returned instead.
|
||||
pub fn take(&mut self, length: usize) -> Option<&[u8]> {
|
||||
if self.left() < length {
|
||||
return None;
|
||||
}
|
||||
let current = self.cursor;
|
||||
self.cursor += length;
|
||||
Some(&self.buffer[current..current + length])
|
||||
}
|
||||
|
||||
/// Used to check whether the reader has any content left
|
||||
/// after the cursor (cursor has not reached end of buffer)
|
||||
pub fn any_left(&self) -> bool {
|
||||
self.cursor < self.buffer.len()
|
||||
}
|
||||
|
||||
/// Returns the cursor position which is also the number
|
||||
/// of bytes that have been read from the buffer.
|
||||
pub fn used(&self) -> usize {
|
||||
self.cursor
|
||||
}
|
||||
|
||||
/// Returns the number of bytes that are still able to be
|
||||
/// read (The number of remaining takes)
|
||||
pub fn left(&self) -> usize {
|
||||
self.buffer.len() - self.cursor
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for implementing encoding and decoding functionality
|
||||
/// on something.
|
||||
pub trait Codec: Debug + Sized {
|
||||
/// Function for encoding itself by appending itself to
|
||||
/// the provided vec of bytes.
|
||||
fn encode(&self, bytes: &mut Vec<u8>);
|
||||
|
||||
/// Function for decoding itself from the provided reader
|
||||
/// will return Some if the decoding was successful or
|
||||
/// None if it was not.
|
||||
fn read(_: &mut Reader) -> Option<Self>;
|
||||
|
||||
/// Convenience function for encoding the implementation
|
||||
/// into a vec and returning it
|
||||
fn get_encoding(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
self.encode(&mut bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
/// Function for wrapping a call to the read function in
|
||||
/// a Reader for the slice of bytes provided
|
||||
fn read_bytes(bytes: &[u8]) -> Option<Self> {
|
||||
let mut reader = Reader::init(bytes);
|
||||
Self::read(&mut reader)
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_u8(bytes: &[u8]) -> Option<u8> {
|
||||
let [value]: [u8; 1] = bytes.try_into().ok()?;
|
||||
Some(value)
|
||||
}
|
||||
|
||||
impl Codec for u8 {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.push(*self);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
r.take(1).and_then(decode_u8)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_u16(v: u16, out: &mut [u8]) {
|
||||
let out: &mut [u8; 2] = (&mut out[..2]).try_into().unwrap();
|
||||
*out = u16::to_be_bytes(v);
|
||||
}
|
||||
|
||||
pub fn decode_u16(bytes: &[u8]) -> Option<u16> {
|
||||
Some(u16::from_be_bytes(bytes.try_into().ok()?))
|
||||
}
|
||||
|
||||
impl Codec for u16 {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
let mut b16 = [0u8; 2];
|
||||
put_u16(*self, &mut b16);
|
||||
bytes.extend_from_slice(&b16);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
r.take(2).and_then(decode_u16)
|
||||
}
|
||||
}
|
||||
|
||||
// Make a distinct type for u24, even though it's a u32 underneath
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct u24(pub u32);
|
||||
|
||||
impl u24 {
|
||||
pub fn decode(bytes: &[u8]) -> Option<Self> {
|
||||
let [a, b, c]: [u8; 3] = bytes.try_into().ok()?;
|
||||
Some(Self(u32::from_be_bytes([0, a, b, c])))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
|
||||
impl From<u24> for usize {
|
||||
#[inline]
|
||||
fn from(v: u24) -> Self {
|
||||
v.0 as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Codec for u24 {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
let be_bytes = u32::to_be_bytes(self.0);
|
||||
bytes.extend_from_slice(&be_bytes[1..])
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
r.take(3).and_then(Self::decode)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_u32(bytes: &[u8]) -> Option<u32> {
|
||||
Some(u32::from_be_bytes(bytes.try_into().ok()?))
|
||||
}
|
||||
|
||||
impl Codec for u32 {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.extend(Self::to_be_bytes(*self))
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
r.take(4).and_then(decode_u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_u64(v: u64, bytes: &mut [u8]) {
|
||||
let bytes: &mut [u8; 8] = (&mut bytes[..8]).try_into().unwrap();
|
||||
*bytes = u64::to_be_bytes(v)
|
||||
}
|
||||
|
||||
pub fn decode_u64(bytes: &[u8]) -> Option<u64> {
|
||||
Some(u64::from_be_bytes(bytes.try_into().ok()?))
|
||||
}
|
||||
|
||||
impl Codec for u64 {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
let mut b64 = [0u8; 8];
|
||||
put_u64(*self, &mut b64);
|
||||
bytes.extend_from_slice(&b64);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
r.take(8).and_then(decode_u64)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_vec_u8<T: Codec>(bytes: &mut Vec<u8>, items: &[T]) {
|
||||
let len_offset = bytes.len();
|
||||
bytes.push(0);
|
||||
|
||||
for i in items {
|
||||
i.encode(bytes);
|
||||
}
|
||||
|
||||
let len = bytes.len() - len_offset - 1;
|
||||
debug_assert!(len <= 0xff);
|
||||
bytes[len_offset] = len as u8;
|
||||
}
|
||||
|
||||
pub fn encode_vec_u16<T: Codec>(bytes: &mut Vec<u8>, items: &[T]) {
|
||||
let len_offset = bytes.len();
|
||||
bytes.extend([0, 0]);
|
||||
|
||||
for i in items {
|
||||
i.encode(bytes);
|
||||
}
|
||||
|
||||
let len = bytes.len() - len_offset - 2;
|
||||
debug_assert!(len <= 0xffff);
|
||||
let out: &mut [u8; 2] = (&mut bytes[len_offset..len_offset + 2])
|
||||
.try_into()
|
||||
.unwrap();
|
||||
*out = u16::to_be_bytes(len as u16);
|
||||
}
|
||||
|
||||
pub fn encode_vec_u24<T: Codec>(bytes: &mut Vec<u8>, items: &[T]) {
|
||||
let len_offset = bytes.len();
|
||||
bytes.extend([0, 0, 0]);
|
||||
|
||||
for i in items {
|
||||
i.encode(bytes);
|
||||
}
|
||||
|
||||
let len = bytes.len() - len_offset - 3;
|
||||
debug_assert!(len <= 0xff_ffff);
|
||||
let len_bytes = u32::to_be_bytes(len as u32);
|
||||
let out: &mut [u8; 3] = (&mut bytes[len_offset..len_offset + 3])
|
||||
.try_into()
|
||||
.unwrap();
|
||||
out.copy_from_slice(&len_bytes[1..]);
|
||||
}
|
||||
|
||||
pub fn read_vec_u8<T: Codec>(r: &mut Reader) -> Option<Vec<T>> {
|
||||
let mut ret: Vec<T> = Vec::new();
|
||||
let len = usize::from(u8::read(r)?);
|
||||
let mut sub = r.sub(len)?;
|
||||
|
||||
while sub.any_left() {
|
||||
ret.push(T::read(&mut sub)?);
|
||||
}
|
||||
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
pub fn read_vec_u16<T: Codec>(r: &mut Reader) -> Option<Vec<T>> {
|
||||
let mut ret: Vec<T> = Vec::new();
|
||||
let len = usize::from(u16::read(r)?);
|
||||
let mut sub = r.sub(len)?;
|
||||
|
||||
while sub.any_left() {
|
||||
ret.push(T::read(&mut sub)?);
|
||||
}
|
||||
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
pub fn read_vec_u24_limited<T: Codec>(r: &mut Reader, max_bytes: usize) -> Option<Vec<T>> {
|
||||
let mut ret: Vec<T> = Vec::new();
|
||||
let len = u24::read(r)?.0 as usize;
|
||||
if len > max_bytes {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut sub = r.sub(len)?;
|
||||
|
||||
while sub.any_left() {
|
||||
ret.push(T::read(&mut sub)?);
|
||||
}
|
||||
|
||||
Some(ret)
|
||||
}
|
||||
425
third_party/rustls-fork-shadow-tls/src/msgs/deframer.rs
vendored
Normal file
425
third_party/rustls-fork-shadow-tls/src/msgs/deframer.rs
vendored
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::msgs::codec;
|
||||
use crate::msgs::message::{MessageError, OpaqueMessage};
|
||||
|
||||
/// This deframer works to reconstruct TLS messages
|
||||
/// from arbitrary-sized reads, buffering as necessary.
|
||||
/// The input is `read()`, get the output from `pop()`.
|
||||
pub struct MessageDeframer {
|
||||
/// Completed frames for output.
|
||||
frames: VecDeque<OpaqueMessage>,
|
||||
|
||||
/// Set to true if the peer is not talking TLS, but some other
|
||||
/// protocol. The caller should abort the connection, because
|
||||
/// the deframer cannot recover.
|
||||
desynced: bool,
|
||||
|
||||
/// A fixed-size buffer containing the currently-accumulating
|
||||
/// TLS message.
|
||||
buf: Box<[u8; OpaqueMessage::MAX_WIRE_SIZE]>,
|
||||
|
||||
/// What size prefix of `buf` is used.
|
||||
used: usize,
|
||||
}
|
||||
|
||||
impl Default for MessageDeframer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageDeframer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
frames: VecDeque::new(),
|
||||
desynced: false,
|
||||
buf: Box::new([0u8; OpaqueMessage::MAX_WIRE_SIZE]),
|
||||
used: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return any complete messages that the deframer has been able to parse.
|
||||
///
|
||||
/// Returns an `Error` if the deframer failed to parse some message contents,
|
||||
/// `Ok(None)` if no full message is buffered, and `Ok(Some(_))` if a valid message was found.
|
||||
pub fn pop(&mut self) -> Result<Option<OpaqueMessage>, Error> {
|
||||
if self.desynced {
|
||||
return Err(Error::CorruptMessage);
|
||||
} else if let Some(msg) = self.frames.pop_front() {
|
||||
return Ok(Some(msg));
|
||||
}
|
||||
|
||||
let mut taken = 0;
|
||||
loop {
|
||||
// Does our `buf` contain a full message? It does if it is big enough to
|
||||
// contain a header, and that header has a length which falls within `buf`.
|
||||
// If so, deframe it and place the message onto the frames output queue.
|
||||
let mut rd = codec::Reader::init(&self.buf[taken..self.used]);
|
||||
let m = match OpaqueMessage::read(&mut rd) {
|
||||
Ok(m) => m,
|
||||
Err(MessageError::TooShortForHeader | MessageError::TooShortForLength) => break,
|
||||
Err(_) => {
|
||||
self.desynced = true;
|
||||
return Err(Error::CorruptMessage);
|
||||
}
|
||||
};
|
||||
|
||||
taken += rd.used();
|
||||
self.frames.push_back(m);
|
||||
}
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
if taken < self.used {
|
||||
/* Before:
|
||||
* +----------+----------+----------+
|
||||
* | taken | pending |xxxxxxxxxx|
|
||||
* +----------+----------+----------+
|
||||
* 0 ^ taken ^ self.used
|
||||
*
|
||||
* After:
|
||||
* +----------+----------+----------+
|
||||
* | pending |xxxxxxxxxxxxxxxxxxxxx|
|
||||
* +----------+----------+----------+
|
||||
* 0 ^ self.used
|
||||
*/
|
||||
|
||||
self.buf
|
||||
.copy_within(taken..self.used, 0);
|
||||
self.used -= taken;
|
||||
} else if taken == self.used {
|
||||
self.used = 0;
|
||||
}
|
||||
|
||||
Ok(self.frames.pop_front())
|
||||
}
|
||||
|
||||
/// Read some bytes from `rd`, and add them to our internal buffer.
|
||||
#[allow(clippy::comparison_chain)]
|
||||
pub fn read(&mut self, rd: &mut dyn io::Read) -> io::Result<usize> {
|
||||
if self.used == OpaqueMessage::MAX_WIRE_SIZE {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "message buffer full"));
|
||||
}
|
||||
|
||||
// Try to do the largest reads possible. Note that if
|
||||
// we get a message with a length field out of range here,
|
||||
// we do a zero length read. That looks like an EOF to
|
||||
// the next layer up, which is fine.
|
||||
debug_assert!(self.used <= OpaqueMessage::MAX_WIRE_SIZE);
|
||||
let new_bytes = rd.read(&mut self.buf[self.used..])?;
|
||||
self.used += new_bytes;
|
||||
Ok(new_bytes)
|
||||
}
|
||||
|
||||
/// Returns true if we have messages for the caller
|
||||
/// to process, either whole messages in our output
|
||||
/// queue or partial messages in our buffer.
|
||||
pub fn has_pending(&self) -> bool {
|
||||
!self.frames.is_empty() || self.used > 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::MessageDeframer;
|
||||
use crate::msgs::message::{Message, OpaqueMessage};
|
||||
use crate::{msgs, Error};
|
||||
use std::convert::TryFrom;
|
||||
use std::io;
|
||||
|
||||
const FIRST_MESSAGE: &[u8] = include_bytes!("../testdata/deframer-test.1.bin");
|
||||
const SECOND_MESSAGE: &[u8] = include_bytes!("../testdata/deframer-test.2.bin");
|
||||
|
||||
const EMPTY_APPLICATIONDATA_MESSAGE: &[u8] =
|
||||
include_bytes!("../testdata/deframer-empty-applicationdata.bin");
|
||||
|
||||
const INVALID_EMPTY_MESSAGE: &[u8] = include_bytes!("../testdata/deframer-invalid-empty.bin");
|
||||
const INVALID_CONTENTTYPE_MESSAGE: &[u8] =
|
||||
include_bytes!("../testdata/deframer-invalid-contenttype.bin");
|
||||
const INVALID_VERSION_MESSAGE: &[u8] =
|
||||
include_bytes!("../testdata/deframer-invalid-version.bin");
|
||||
const INVALID_LENGTH_MESSAGE: &[u8] = include_bytes!("../testdata/deframer-invalid-length.bin");
|
||||
|
||||
struct ByteRead<'a> {
|
||||
buf: &'a [u8],
|
||||
offs: usize,
|
||||
}
|
||||
|
||||
impl<'a> ByteRead<'a> {
|
||||
fn new(bytes: &'a [u8]) -> Self {
|
||||
ByteRead {
|
||||
buf: bytes,
|
||||
offs: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> io::Read for ByteRead<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let mut len = 0;
|
||||
|
||||
while len < buf.len() && len < self.buf.len() - self.offs {
|
||||
buf[len] = self.buf[self.offs + len];
|
||||
len += 1;
|
||||
}
|
||||
|
||||
self.offs += len;
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn input_bytes(d: &mut MessageDeframer, bytes: &[u8]) -> io::Result<usize> {
|
||||
let mut rd = ByteRead::new(bytes);
|
||||
d.read(&mut rd)
|
||||
}
|
||||
|
||||
fn input_bytes_concat(
|
||||
d: &mut MessageDeframer,
|
||||
bytes1: &[u8],
|
||||
bytes2: &[u8],
|
||||
) -> io::Result<usize> {
|
||||
let mut bytes = vec![0u8; bytes1.len() + bytes2.len()];
|
||||
bytes[..bytes1.len()].clone_from_slice(bytes1);
|
||||
bytes[bytes1.len()..].clone_from_slice(bytes2);
|
||||
let mut rd = ByteRead::new(&bytes);
|
||||
d.read(&mut rd)
|
||||
}
|
||||
|
||||
struct ErrorRead {
|
||||
error: Option<io::Error>,
|
||||
}
|
||||
|
||||
impl ErrorRead {
|
||||
fn new(error: io::Error) -> Self {
|
||||
Self { error: Some(error) }
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Read for ErrorRead {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
for (i, b) in buf.iter_mut().enumerate() {
|
||||
*b = i as u8;
|
||||
}
|
||||
|
||||
let error = self.error.take().unwrap();
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
|
||||
fn input_error(d: &mut MessageDeframer) {
|
||||
let error = io::Error::from(io::ErrorKind::TimedOut);
|
||||
let mut rd = ErrorRead::new(error);
|
||||
d.read(&mut rd)
|
||||
.expect_err("error not propagated");
|
||||
}
|
||||
|
||||
fn input_whole_incremental(d: &mut MessageDeframer, bytes: &[u8]) {
|
||||
let before = d.used;
|
||||
|
||||
for i in 0..bytes.len() {
|
||||
assert_len(1, input_bytes(d, &bytes[i..i + 1]));
|
||||
assert!(d.has_pending());
|
||||
}
|
||||
|
||||
assert_eq!(before + bytes.len(), d.used);
|
||||
}
|
||||
|
||||
fn assert_len(want: usize, got: io::Result<usize>) {
|
||||
if let Ok(gotval) = got {
|
||||
assert_eq!(gotval, want);
|
||||
} else {
|
||||
panic!("read failed, expected {:?} bytes", want);
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_first(d: &mut MessageDeframer) {
|
||||
let m = d.pop().unwrap().unwrap();
|
||||
assert_eq!(m.typ, msgs::enums::ContentType::Handshake);
|
||||
Message::try_from(m.into_plain_message()).unwrap();
|
||||
}
|
||||
|
||||
fn pop_second(d: &mut MessageDeframer) {
|
||||
let m = d.pop().unwrap().unwrap();
|
||||
assert_eq!(m.typ, msgs::enums::ContentType::Alert);
|
||||
Message::try_from(m.into_plain_message()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_incremental() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert!(!d.has_pending());
|
||||
input_whole_incremental(&mut d, FIRST_MESSAGE);
|
||||
assert!(d.has_pending());
|
||||
assert_eq!(0, d.frames.len());
|
||||
pop_first(&mut d);
|
||||
assert!(!d.has_pending());
|
||||
assert!(!d.desynced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_incremental_2() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert!(!d.has_pending());
|
||||
input_whole_incremental(&mut d, FIRST_MESSAGE);
|
||||
assert!(d.has_pending());
|
||||
input_whole_incremental(&mut d, SECOND_MESSAGE);
|
||||
assert!(d.has_pending());
|
||||
assert_eq!(0, d.frames.len());
|
||||
pop_first(&mut d);
|
||||
assert!(d.has_pending());
|
||||
assert_eq!(1, d.frames.len());
|
||||
pop_second(&mut d);
|
||||
assert!(!d.has_pending());
|
||||
assert!(!d.desynced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_whole() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert!(!d.has_pending());
|
||||
assert_len(FIRST_MESSAGE.len(), input_bytes(&mut d, FIRST_MESSAGE));
|
||||
assert!(d.has_pending());
|
||||
assert_eq!(d.frames.len(), 0);
|
||||
pop_first(&mut d);
|
||||
assert!(!d.has_pending());
|
||||
assert!(!d.desynced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_whole_2() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert!(!d.has_pending());
|
||||
assert_len(FIRST_MESSAGE.len(), input_bytes(&mut d, FIRST_MESSAGE));
|
||||
assert_len(SECOND_MESSAGE.len(), input_bytes(&mut d, SECOND_MESSAGE));
|
||||
assert_eq!(d.frames.len(), 0);
|
||||
pop_first(&mut d);
|
||||
assert_eq!(d.frames.len(), 1);
|
||||
pop_second(&mut d);
|
||||
assert!(!d.has_pending());
|
||||
assert!(!d.desynced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_in_one_read() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert!(!d.has_pending());
|
||||
assert_len(
|
||||
FIRST_MESSAGE.len() + SECOND_MESSAGE.len(),
|
||||
input_bytes_concat(&mut d, FIRST_MESSAGE, SECOND_MESSAGE),
|
||||
);
|
||||
assert_eq!(d.frames.len(), 0);
|
||||
pop_first(&mut d);
|
||||
assert_eq!(d.frames.len(), 1);
|
||||
pop_second(&mut d);
|
||||
assert!(!d.has_pending());
|
||||
assert!(!d.desynced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_in_one_read_shortest_first() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert!(!d.has_pending());
|
||||
assert_len(
|
||||
FIRST_MESSAGE.len() + SECOND_MESSAGE.len(),
|
||||
input_bytes_concat(&mut d, SECOND_MESSAGE, FIRST_MESSAGE),
|
||||
);
|
||||
assert_eq!(d.frames.len(), 0);
|
||||
pop_second(&mut d);
|
||||
assert_eq!(d.frames.len(), 1);
|
||||
pop_first(&mut d);
|
||||
assert!(!d.has_pending());
|
||||
assert!(!d.desynced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_incremental_with_nonfatal_read_error() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert_len(3, input_bytes(&mut d, &FIRST_MESSAGE[..3]));
|
||||
input_error(&mut d);
|
||||
assert_len(
|
||||
FIRST_MESSAGE.len() - 3,
|
||||
input_bytes(&mut d, &FIRST_MESSAGE[3..]),
|
||||
);
|
||||
assert_eq!(d.frames.len(), 0);
|
||||
pop_first(&mut d);
|
||||
assert!(!d.has_pending());
|
||||
assert!(!d.desynced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_contenttype_errors() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert_len(
|
||||
INVALID_CONTENTTYPE_MESSAGE.len(),
|
||||
input_bytes(&mut d, INVALID_CONTENTTYPE_MESSAGE),
|
||||
);
|
||||
assert_eq!(d.pop().unwrap_err(), Error::CorruptMessage);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_version_errors() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert_len(
|
||||
INVALID_VERSION_MESSAGE.len(),
|
||||
input_bytes(&mut d, INVALID_VERSION_MESSAGE),
|
||||
);
|
||||
assert_eq!(d.pop().unwrap_err(), Error::CorruptMessage);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_length_errors() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert_len(
|
||||
INVALID_LENGTH_MESSAGE.len(),
|
||||
input_bytes(&mut d, INVALID_LENGTH_MESSAGE),
|
||||
);
|
||||
assert_eq!(d.pop().unwrap_err(), Error::CorruptMessage);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_applicationdata() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert_len(
|
||||
EMPTY_APPLICATIONDATA_MESSAGE.len(),
|
||||
input_bytes(&mut d, EMPTY_APPLICATIONDATA_MESSAGE),
|
||||
);
|
||||
let m = d.pop().unwrap().unwrap();
|
||||
assert_eq!(m.typ, msgs::enums::ContentType::ApplicationData);
|
||||
assert_eq!(m.payload.0.len(), 0);
|
||||
assert!(!d.has_pending());
|
||||
assert!(!d.desynced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_empty_errors() {
|
||||
let mut d = MessageDeframer::new();
|
||||
assert_len(
|
||||
INVALID_EMPTY_MESSAGE.len(),
|
||||
input_bytes(&mut d, INVALID_EMPTY_MESSAGE),
|
||||
);
|
||||
assert_eq!(d.pop().unwrap_err(), Error::CorruptMessage);
|
||||
// CorruptMessage has been fused
|
||||
assert_eq!(d.pop().unwrap_err(), Error::CorruptMessage);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_limited_buffer() {
|
||||
const PAYLOAD_LEN: usize = 16_384;
|
||||
let mut message = Vec::with_capacity(8192);
|
||||
message.push(0x17); // ApplicationData
|
||||
message.extend(&[0x03, 0x04]); // ProtocolVersion
|
||||
message.extend((PAYLOAD_LEN as u16).to_be_bytes()); // payload length
|
||||
message.extend(&[0; PAYLOAD_LEN]);
|
||||
|
||||
let mut d = MessageDeframer::new();
|
||||
assert_len(message.len(), input_bytes(&mut d, &message));
|
||||
assert_len(
|
||||
OpaqueMessage::MAX_WIRE_SIZE - 16_389,
|
||||
input_bytes(&mut d, &message),
|
||||
);
|
||||
assert!(input_bytes(&mut d, &message).is_err());
|
||||
}
|
||||
}
|
||||
375
third_party/rustls-fork-shadow-tls/src/msgs/enums.rs
vendored
Normal file
375
third_party/rustls-fork-shadow-tls/src/msgs/enums.rs
vendored
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![allow(non_camel_case_types)]
|
||||
/// This file is autogenerated. See https://github.com/ctz/tls-hacking/
|
||||
use crate::msgs::codec::{Codec, Reader};
|
||||
|
||||
enum_builder! {
|
||||
/// The `HashAlgorithm` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: HashAlgorithm;
|
||||
EnumVal{
|
||||
NONE => 0x00,
|
||||
MD5 => 0x01,
|
||||
SHA1 => 0x02,
|
||||
SHA224 => 0x03,
|
||||
SHA256 => 0x04,
|
||||
SHA384 => 0x05,
|
||||
SHA512 => 0x06
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `SignatureAlgorithm` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: SignatureAlgorithm;
|
||||
EnumVal{
|
||||
Anonymous => 0x00,
|
||||
RSA => 0x01,
|
||||
DSA => 0x02,
|
||||
ECDSA => 0x03,
|
||||
ED25519 => 0x07,
|
||||
ED448 => 0x08
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `ClientCertificateType` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: ClientCertificateType;
|
||||
EnumVal{
|
||||
RSASign => 0x01,
|
||||
DSSSign => 0x02,
|
||||
RSAFixedDH => 0x03,
|
||||
DSSFixedDH => 0x04,
|
||||
RSAEphemeralDH => 0x05,
|
||||
DSSEphemeralDH => 0x06,
|
||||
FortezzaDMS => 0x14,
|
||||
ECDSASign => 0x40,
|
||||
RSAFixedECDH => 0x41,
|
||||
ECDSAFixedECDH => 0x42
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `Compression` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: Compression;
|
||||
EnumVal{
|
||||
Null => 0x00,
|
||||
Deflate => 0x01,
|
||||
LSZ => 0x40
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `ContentType` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: ContentType;
|
||||
EnumVal{
|
||||
ChangeCipherSpec => 0x14,
|
||||
Alert => 0x15,
|
||||
Handshake => 0x16,
|
||||
ApplicationData => 0x17,
|
||||
Heartbeat => 0x18
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `HandshakeType` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: HandshakeType;
|
||||
EnumVal{
|
||||
HelloRequest => 0x00,
|
||||
ClientHello => 0x01,
|
||||
ServerHello => 0x02,
|
||||
HelloVerifyRequest => 0x03,
|
||||
NewSessionTicket => 0x04,
|
||||
EndOfEarlyData => 0x05,
|
||||
HelloRetryRequest => 0x06,
|
||||
EncryptedExtensions => 0x08,
|
||||
Certificate => 0x0b,
|
||||
ServerKeyExchange => 0x0c,
|
||||
CertificateRequest => 0x0d,
|
||||
ServerHelloDone => 0x0e,
|
||||
CertificateVerify => 0x0f,
|
||||
ClientKeyExchange => 0x10,
|
||||
Finished => 0x14,
|
||||
CertificateURL => 0x15,
|
||||
CertificateStatus => 0x16,
|
||||
KeyUpdate => 0x18,
|
||||
MessageHash => 0xfe
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `AlertLevel` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: AlertLevel;
|
||||
EnumVal{
|
||||
Warning => 0x01,
|
||||
Fatal => 0x02
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `AlertDescription` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: AlertDescription;
|
||||
EnumVal{
|
||||
CloseNotify => 0x00,
|
||||
UnexpectedMessage => 0x0a,
|
||||
BadRecordMac => 0x14,
|
||||
DecryptionFailed => 0x15,
|
||||
RecordOverflow => 0x16,
|
||||
DecompressionFailure => 0x1e,
|
||||
HandshakeFailure => 0x28,
|
||||
NoCertificate => 0x29,
|
||||
BadCertificate => 0x2a,
|
||||
UnsupportedCertificate => 0x2b,
|
||||
CertificateRevoked => 0x2c,
|
||||
CertificateExpired => 0x2d,
|
||||
CertificateUnknown => 0x2e,
|
||||
IllegalParameter => 0x2f,
|
||||
UnknownCA => 0x30,
|
||||
AccessDenied => 0x31,
|
||||
DecodeError => 0x32,
|
||||
DecryptError => 0x33,
|
||||
ExportRestriction => 0x3c,
|
||||
ProtocolVersion => 0x46,
|
||||
InsufficientSecurity => 0x47,
|
||||
InternalError => 0x50,
|
||||
InappropriateFallback => 0x56,
|
||||
UserCanceled => 0x5a,
|
||||
NoRenegotiation => 0x64,
|
||||
MissingExtension => 0x6d,
|
||||
UnsupportedExtension => 0x6e,
|
||||
CertificateUnobtainable => 0x6f,
|
||||
UnrecognisedName => 0x70,
|
||||
BadCertificateStatusResponse => 0x71,
|
||||
BadCertificateHashValue => 0x72,
|
||||
UnknownPSKIdentity => 0x73,
|
||||
CertificateRequired => 0x74,
|
||||
NoApplicationProtocol => 0x78
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `HeartbeatMessageType` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: HeartbeatMessageType;
|
||||
EnumVal{
|
||||
Request => 0x01,
|
||||
Response => 0x02
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `ExtensionType` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U16
|
||||
EnumName: ExtensionType;
|
||||
EnumVal{
|
||||
ServerName => 0x0000,
|
||||
MaxFragmentLength => 0x0001,
|
||||
ClientCertificateUrl => 0x0002,
|
||||
TrustedCAKeys => 0x0003,
|
||||
TruncatedHMAC => 0x0004,
|
||||
StatusRequest => 0x0005,
|
||||
UserMapping => 0x0006,
|
||||
ClientAuthz => 0x0007,
|
||||
ServerAuthz => 0x0008,
|
||||
CertificateType => 0x0009,
|
||||
EllipticCurves => 0x000a,
|
||||
ECPointFormats => 0x000b,
|
||||
SRP => 0x000c,
|
||||
SignatureAlgorithms => 0x000d,
|
||||
UseSRTP => 0x000e,
|
||||
Heartbeat => 0x000f,
|
||||
ALProtocolNegotiation => 0x0010,
|
||||
SCT => 0x0012,
|
||||
Padding => 0x0015,
|
||||
ExtendedMasterSecret => 0x0017,
|
||||
SessionTicket => 0x0023,
|
||||
PreSharedKey => 0x0029,
|
||||
EarlyData => 0x002a,
|
||||
SupportedVersions => 0x002b,
|
||||
Cookie => 0x002c,
|
||||
PSKKeyExchangeModes => 0x002d,
|
||||
TicketEarlyDataInfo => 0x002e,
|
||||
CertificateAuthorities => 0x002f,
|
||||
OIDFilters => 0x0030,
|
||||
PostHandshakeAuth => 0x0031,
|
||||
SignatureAlgorithmsCert => 0x0032,
|
||||
KeyShare => 0x0033,
|
||||
TransportParameters => 0x0039,
|
||||
NextProtocolNegotiation => 0x3374,
|
||||
ChannelId => 0x754f,
|
||||
RenegotiationInfo => 0xff01,
|
||||
TransportParametersDraft => 0xffa5
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `ServerNameType` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: ServerNameType;
|
||||
EnumVal{
|
||||
HostName => 0x00
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `NamedCurve` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U16
|
||||
EnumName: NamedCurve;
|
||||
EnumVal{
|
||||
sect163k1 => 0x0001,
|
||||
sect163r1 => 0x0002,
|
||||
sect163r2 => 0x0003,
|
||||
sect193r1 => 0x0004,
|
||||
sect193r2 => 0x0005,
|
||||
sect233k1 => 0x0006,
|
||||
sect233r1 => 0x0007,
|
||||
sect239k1 => 0x0008,
|
||||
sect283k1 => 0x0009,
|
||||
sect283r1 => 0x000a,
|
||||
sect409k1 => 0x000b,
|
||||
sect409r1 => 0x000c,
|
||||
sect571k1 => 0x000d,
|
||||
sect571r1 => 0x000e,
|
||||
secp160k1 => 0x000f,
|
||||
secp160r1 => 0x0010,
|
||||
secp160r2 => 0x0011,
|
||||
secp192k1 => 0x0012,
|
||||
secp192r1 => 0x0013,
|
||||
secp224k1 => 0x0014,
|
||||
secp224r1 => 0x0015,
|
||||
secp256k1 => 0x0016,
|
||||
secp256r1 => 0x0017,
|
||||
secp384r1 => 0x0018,
|
||||
secp521r1 => 0x0019,
|
||||
brainpoolp256r1 => 0x001a,
|
||||
brainpoolp384r1 => 0x001b,
|
||||
brainpoolp512r1 => 0x001c,
|
||||
X25519 => 0x001d,
|
||||
X448 => 0x001e,
|
||||
arbitrary_explicit_prime_curves => 0xff01,
|
||||
arbitrary_explicit_char2_curves => 0xff02
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `NamedGroup` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U16
|
||||
EnumName: NamedGroup;
|
||||
EnumVal{
|
||||
secp256r1 => 0x0017,
|
||||
secp384r1 => 0x0018,
|
||||
secp521r1 => 0x0019,
|
||||
X25519 => 0x001d,
|
||||
X448 => 0x001e,
|
||||
FFDHE2048 => 0x0100,
|
||||
FFDHE3072 => 0x0101,
|
||||
FFDHE4096 => 0x0102,
|
||||
FFDHE6144 => 0x0103,
|
||||
FFDHE8192 => 0x0104
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `ECPointFormat` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: ECPointFormat;
|
||||
EnumVal{
|
||||
Uncompressed => 0x00,
|
||||
ANSIX962CompressedPrime => 0x01,
|
||||
ANSIX962CompressedChar2 => 0x02
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `HeartbeatMode` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: HeartbeatMode;
|
||||
EnumVal{
|
||||
PeerAllowedToSend => 0x01,
|
||||
PeerNotAllowedToSend => 0x02
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `ECCurveType` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: ECCurveType;
|
||||
EnumVal{
|
||||
ExplicitPrime => 0x01,
|
||||
ExplicitChar2 => 0x02,
|
||||
NamedCurve => 0x03
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `PSKKeyExchangeMode` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: PSKKeyExchangeMode;
|
||||
EnumVal{
|
||||
PSK_KE => 0x00,
|
||||
PSK_DHE_KE => 0x01
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `KeyUpdateRequest` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: KeyUpdateRequest;
|
||||
EnumVal{
|
||||
UpdateNotRequested => 0x00,
|
||||
UpdateRequested => 0x01
|
||||
}
|
||||
}
|
||||
|
||||
enum_builder! {
|
||||
/// The `CertificateStatusType` TLS protocol enum. Values in this enum are taken
|
||||
/// from the various RFCs covering TLS, and are listed by IANA.
|
||||
/// The `Unknown` item is used when processing unrecognised ordinals.
|
||||
@U8
|
||||
EnumName: CertificateStatusType;
|
||||
EnumVal{
|
||||
OCSP => 0x01
|
||||
}
|
||||
}
|
||||
88
third_party/rustls-fork-shadow-tls/src/msgs/enums_test.rs
vendored
Normal file
88
third_party/rustls-fork-shadow-tls/src/msgs/enums_test.rs
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/// These tests are intended to provide coverage and
|
||||
/// check panic-safety of relatively unused values.
|
||||
use super::codec::Codec;
|
||||
use super::enums::*;
|
||||
|
||||
fn get8<T: Codec>(enum_value: &T) -> u8 {
|
||||
let enc = enum_value.get_encoding();
|
||||
assert_eq!(enc.len(), 1);
|
||||
enc[0]
|
||||
}
|
||||
|
||||
fn get16<T: Codec>(enum_value: &T) -> u16 {
|
||||
let enc = enum_value.get_encoding();
|
||||
assert_eq!(enc.len(), 2);
|
||||
(enc[0] as u16 >> 8) | (enc[1] as u16)
|
||||
}
|
||||
|
||||
fn test_enum16<T: Codec>(first: T, last: T) {
|
||||
let first_v = get16(&first);
|
||||
let last_v = get16(&last);
|
||||
|
||||
for val in first_v..last_v + 1 {
|
||||
let mut buf = Vec::new();
|
||||
val.encode(&mut buf);
|
||||
assert_eq!(buf.len(), 2);
|
||||
|
||||
let t = T::read_bytes(&buf).unwrap();
|
||||
assert_eq!(val, get16(&t));
|
||||
}
|
||||
}
|
||||
|
||||
fn test_enum8<T: Codec>(first: T, last: T) {
|
||||
let first_v = get8(&first);
|
||||
let last_v = get8(&last);
|
||||
|
||||
for val in first_v..last_v + 1 {
|
||||
let mut buf = Vec::new();
|
||||
val.encode(&mut buf);
|
||||
assert_eq!(buf.len(), 1);
|
||||
|
||||
let t = T::read_bytes(&buf).unwrap();
|
||||
assert_eq!(val, get8(&t));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enums() {
|
||||
test_enum8::<HashAlgorithm>(HashAlgorithm::NONE, HashAlgorithm::SHA512);
|
||||
test_enum8::<SignatureAlgorithm>(SignatureAlgorithm::Anonymous, SignatureAlgorithm::ECDSA);
|
||||
test_enum8::<ClientCertificateType>(
|
||||
ClientCertificateType::RSASign,
|
||||
ClientCertificateType::ECDSAFixedECDH,
|
||||
);
|
||||
test_enum8::<Compression>(Compression::Null, Compression::LSZ);
|
||||
test_enum8::<ContentType>(ContentType::ChangeCipherSpec, ContentType::Heartbeat);
|
||||
test_enum8::<HandshakeType>(HandshakeType::HelloRequest, HandshakeType::MessageHash);
|
||||
test_enum8::<AlertLevel>(AlertLevel::Warning, AlertLevel::Fatal);
|
||||
test_enum8::<AlertDescription>(
|
||||
AlertDescription::CloseNotify,
|
||||
AlertDescription::NoApplicationProtocol,
|
||||
);
|
||||
test_enum8::<HeartbeatMessageType>(
|
||||
HeartbeatMessageType::Request,
|
||||
HeartbeatMessageType::Response,
|
||||
);
|
||||
test_enum16::<ExtensionType>(ExtensionType::ServerName, ExtensionType::RenegotiationInfo);
|
||||
test_enum8::<ServerNameType>(ServerNameType::HostName, ServerNameType::HostName);
|
||||
test_enum16::<NamedCurve>(
|
||||
NamedCurve::sect163k1,
|
||||
NamedCurve::arbitrary_explicit_char2_curves,
|
||||
);
|
||||
test_enum16::<NamedGroup>(NamedGroup::secp256r1, NamedGroup::FFDHE8192);
|
||||
test_enum8::<ECPointFormat>(
|
||||
ECPointFormat::Uncompressed,
|
||||
ECPointFormat::ANSIX962CompressedChar2,
|
||||
);
|
||||
test_enum8::<HeartbeatMode>(
|
||||
HeartbeatMode::PeerAllowedToSend,
|
||||
HeartbeatMode::PeerNotAllowedToSend,
|
||||
);
|
||||
test_enum8::<ECCurveType>(ECCurveType::ExplicitPrime, ECCurveType::NamedCurve);
|
||||
test_enum8::<PSKKeyExchangeMode>(PSKKeyExchangeMode::PSK_KE, PSKKeyExchangeMode::PSK_DHE_KE);
|
||||
test_enum8::<KeyUpdateRequest>(
|
||||
KeyUpdateRequest::UpdateNotRequested,
|
||||
KeyUpdateRequest::UpdateRequested,
|
||||
);
|
||||
test_enum8::<CertificateStatusType>(CertificateStatusType::OCSP, CertificateStatusType::OCSP);
|
||||
}
|
||||
162
third_party/rustls-fork-shadow-tls/src/msgs/fragmenter.rs
vendored
Normal file
162
third_party/rustls-fork-shadow-tls/src/msgs/fragmenter.rs
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
use crate::enums::ProtocolVersion;
|
||||
use crate::msgs::enums::ContentType;
|
||||
use crate::msgs::message::{BorrowedPlainMessage, PlainMessage};
|
||||
use crate::Error;
|
||||
pub const MAX_FRAGMENT_LEN: usize = 16384;
|
||||
pub const PACKET_OVERHEAD: usize = 1 + 2 + 2;
|
||||
pub const MAX_FRAGMENT_SIZE: usize = MAX_FRAGMENT_LEN + PACKET_OVERHEAD;
|
||||
|
||||
pub struct MessageFragmenter {
|
||||
max_frag: usize,
|
||||
}
|
||||
|
||||
impl Default for MessageFragmenter {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_frag: MAX_FRAGMENT_LEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageFragmenter {
|
||||
/// Take the Message `msg` and re-fragment it into new
|
||||
/// messages whose fragment is no more than max_frag.
|
||||
/// Return an iterator across those messages.
|
||||
/// Payloads are borrowed.
|
||||
pub fn fragment_message<'a>(
|
||||
&self,
|
||||
msg: &'a PlainMessage,
|
||||
) -> impl Iterator<Item = BorrowedPlainMessage<'a>> + 'a {
|
||||
self.fragment_slice(msg.typ, msg.version, &msg.payload.0)
|
||||
}
|
||||
|
||||
/// Enqueue borrowed fragments of (version, typ, payload) which
|
||||
/// are no longer than max_frag onto the `out` deque.
|
||||
pub fn fragment_slice<'a>(
|
||||
&self,
|
||||
typ: ContentType,
|
||||
version: ProtocolVersion,
|
||||
payload: &'a [u8],
|
||||
) -> impl Iterator<Item = BorrowedPlainMessage<'a>> + 'a {
|
||||
payload
|
||||
.chunks(self.max_frag)
|
||||
.map(move |c| BorrowedPlainMessage {
|
||||
typ,
|
||||
version,
|
||||
payload: c,
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the maximum fragment size that will be produced.
|
||||
///
|
||||
/// This includes overhead. A `max_fragment_size` of 10 will produce TLS fragments
|
||||
/// up to 10 bytes long.
|
||||
///
|
||||
/// A `max_fragment_size` of `None` sets the highest allowable fragment size.
|
||||
///
|
||||
/// Returns BadMaxFragmentSize if the size is smaller than 32 or larger than 16389.
|
||||
pub fn set_max_fragment_size(&mut self, max_fragment_size: Option<usize>) -> Result<(), Error> {
|
||||
self.max_frag = match max_fragment_size {
|
||||
Some(sz @ 32..=MAX_FRAGMENT_SIZE) => sz - PACKET_OVERHEAD,
|
||||
None => MAX_FRAGMENT_LEN,
|
||||
_ => return Err(Error::BadMaxFragmentSize),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{MessageFragmenter, PACKET_OVERHEAD};
|
||||
use crate::enums::ProtocolVersion;
|
||||
use crate::msgs::base::Payload;
|
||||
use crate::msgs::enums::ContentType;
|
||||
use crate::msgs::message::{BorrowedPlainMessage, PlainMessage};
|
||||
|
||||
fn msg_eq(
|
||||
m: &BorrowedPlainMessage,
|
||||
total_len: usize,
|
||||
typ: &ContentType,
|
||||
version: &ProtocolVersion,
|
||||
bytes: &[u8],
|
||||
) {
|
||||
assert_eq!(&m.typ, typ);
|
||||
assert_eq!(&m.version, version);
|
||||
assert_eq!(m.payload, bytes);
|
||||
|
||||
let buf = m.to_unencrypted_opaque().encode();
|
||||
|
||||
assert_eq!(total_len, buf.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let typ = ContentType::Handshake;
|
||||
let version = ProtocolVersion::TLSv1_2;
|
||||
let data: Vec<u8> = (1..70u8).collect();
|
||||
let m = PlainMessage {
|
||||
typ,
|
||||
version,
|
||||
payload: Payload::new(data),
|
||||
};
|
||||
|
||||
let mut frag = MessageFragmenter::default();
|
||||
frag.set_max_fragment_size(Some(32))
|
||||
.unwrap();
|
||||
let q = frag
|
||||
.fragment_message(&m)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(q.len(), 3);
|
||||
msg_eq(
|
||||
&q[0],
|
||||
32,
|
||||
&typ,
|
||||
&version,
|
||||
&[
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27,
|
||||
],
|
||||
);
|
||||
msg_eq(
|
||||
&q[1],
|
||||
32,
|
||||
&typ,
|
||||
&version,
|
||||
&[
|
||||
28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, 52, 53, 54,
|
||||
],
|
||||
);
|
||||
msg_eq(
|
||||
&q[2],
|
||||
20,
|
||||
&typ,
|
||||
&version,
|
||||
&[55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_fragment() {
|
||||
let m = PlainMessage {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"\x01\x02\x03\x04\x05\x06\x07\x08".to_vec()),
|
||||
};
|
||||
|
||||
let mut frag = MessageFragmenter::default();
|
||||
frag.set_max_fragment_size(Some(32))
|
||||
.unwrap();
|
||||
let q = frag
|
||||
.fragment_message(&m)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(q.len(), 1);
|
||||
msg_eq(
|
||||
&q[0],
|
||||
PACKET_OVERHEAD + 8,
|
||||
&ContentType::Handshake,
|
||||
&ProtocolVersion::TLSv1_2,
|
||||
b"\x01\x02\x03\x04\x05\x06\x07\x08",
|
||||
);
|
||||
}
|
||||
}
|
||||
BIN
third_party/rustls-fork-shadow-tls/src/msgs/handshake-test.1.bin
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/msgs/handshake-test.1.bin
vendored
Normal file
Binary file not shown.
2380
third_party/rustls-fork-shadow-tls/src/msgs/handshake.rs
vendored
Normal file
2380
third_party/rustls-fork-shadow-tls/src/msgs/handshake.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
1247
third_party/rustls-fork-shadow-tls/src/msgs/handshake_test.rs
vendored
Normal file
1247
third_party/rustls-fork-shadow-tls/src/msgs/handshake_test.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
BIN
third_party/rustls-fork-shadow-tls/src/msgs/hello-api.devicecheck.apple.com.bin
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/msgs/hello-api.devicecheck.apple.com.bin
vendored
Normal file
Binary file not shown.
281
third_party/rustls-fork-shadow-tls/src/msgs/hsjoiner.rs
vendored
Normal file
281
third_party/rustls-fork-shadow-tls/src/msgs/hsjoiner.rs
vendored
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use crate::enums::ProtocolVersion;
|
||||
use crate::msgs::base::Payload;
|
||||
use crate::msgs::codec;
|
||||
use crate::msgs::enums::ContentType;
|
||||
use crate::msgs::handshake::HandshakeMessagePayload;
|
||||
use crate::msgs::message::{Message, MessagePayload, PlainMessage};
|
||||
|
||||
const HEADER_SIZE: usize = 1 + 3;
|
||||
|
||||
/// TLS allows for handshake messages of up to 16MB. We
|
||||
/// restrict that to 64KB to limit potential for denial-of-
|
||||
/// service.
|
||||
const MAX_HANDSHAKE_SIZE: u32 = 0xffff;
|
||||
|
||||
/// This works to reconstruct TLS handshake messages
|
||||
/// from individual TLS messages. It's guaranteed that
|
||||
/// TLS messages output from this layer contain precisely
|
||||
/// one handshake payload.
|
||||
pub struct HandshakeJoiner {
|
||||
/// The message payload(s) we're currently accumulating.
|
||||
buf: Vec<u8>,
|
||||
|
||||
/// Sizes of messages currently in the buffer.
|
||||
///
|
||||
/// The buffer can be larger than the sum of the sizes in this queue, because it might contain
|
||||
/// the start of a message that hasn't fully been received yet as its suffix.
|
||||
sizes: VecDeque<usize>,
|
||||
|
||||
/// Version of the protocol we're currently parsing.
|
||||
version: ProtocolVersion,
|
||||
}
|
||||
|
||||
impl HandshakeJoiner {
|
||||
/// Make a new HandshakeJoiner.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
buf: Vec::new(),
|
||||
sizes: VecDeque::new(),
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the message, and join/split it as needed.
|
||||
///
|
||||
/// Returns `Err(JoinerError::Unwanted(msg))` if `msg`'s type is not `ContentType::Handshake` or
|
||||
/// `JoinerError::Decode` if a received payload has an advertised size larger than we accept.
|
||||
///
|
||||
/// Otherwise, yields a `bool` to indicate whether the handshake is "aligned": if the buffer currently
|
||||
/// only contains complete payloads (that is, no incomplete message in the suffix).
|
||||
pub fn push(&mut self, msg: PlainMessage) -> Result<bool, JoinerError> {
|
||||
if msg.typ != ContentType::Handshake {
|
||||
return Err(JoinerError::Unwanted(msg));
|
||||
}
|
||||
|
||||
// The vast majority of the time `self.buf` will be empty since most
|
||||
// handshake messages arrive in a single fragment. Avoid allocating and
|
||||
// copying in that common case.
|
||||
if self.buf.is_empty() {
|
||||
self.buf = msg.payload.0;
|
||||
} else {
|
||||
self.buf
|
||||
.extend_from_slice(&msg.payload.0[..]);
|
||||
}
|
||||
|
||||
if msg.version == ProtocolVersion::TLSv1_3 {
|
||||
self.version = msg.version;
|
||||
}
|
||||
|
||||
// Check the suffix of the buffer that hasn't been covered by `sizes` so far
|
||||
// for complete messages. If we find any, update `self.sizes` and `complete`.
|
||||
let mut complete = self.sizes.iter().copied().sum();
|
||||
while let Some(size) = payload_size(&self.buf[complete..])? {
|
||||
self.sizes.push_back(size);
|
||||
complete += size;
|
||||
}
|
||||
|
||||
// Use the value of `complete` to determine if the buffer currently contains any
|
||||
// incomplete messages. If not, an incoming message is said to be "aligned".
|
||||
Ok(complete == self.buf.len())
|
||||
}
|
||||
|
||||
/// Parse the first received message out of the buffer.
|
||||
///
|
||||
/// Returns `Ok(None)` if we don't have a complete message in the buffer, or `Err` if we
|
||||
/// fail to parse the first message in the buffer.
|
||||
pub fn pop(&mut self) -> Result<Option<Message>, JoinerError> {
|
||||
let len = match self.sizes.pop_front() {
|
||||
Some(len) => len,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
// Parse the first part of the buffer as a handshake buffer.
|
||||
// If we get `None` back, we've failed to parse the message.
|
||||
// If we succeed, drain the relevant bytes from the buffer.
|
||||
|
||||
let buf = &self.buf[..len];
|
||||
let mut rd = codec::Reader::init(buf);
|
||||
let parsed = match HandshakeMessagePayload::read_version(&mut rd, self.version) {
|
||||
Some(p) => p,
|
||||
None => return Err(JoinerError::Decode),
|
||||
};
|
||||
|
||||
let message = Message {
|
||||
version: self.version,
|
||||
payload: MessagePayload::Handshake {
|
||||
parsed,
|
||||
encoded: Payload::new(buf),
|
||||
},
|
||||
};
|
||||
|
||||
self.buf.drain(..len);
|
||||
Ok(Some(message))
|
||||
}
|
||||
}
|
||||
|
||||
/// Does `buf` contain a full handshake payload?
|
||||
///
|
||||
/// Returns `Ok(Some(_))` with the length of the payload (including header) if it does,
|
||||
/// `Ok(None)` if the buffer is too small to contain a message with the length advertised in the
|
||||
/// header, or `Err` if the advertised length is larger than what we want to accept
|
||||
/// (`MAX_HANDSHAKE_SIZE`).
|
||||
fn payload_size(buf: &[u8]) -> Result<Option<usize>, JoinerError> {
|
||||
if buf.len() < HEADER_SIZE {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let (header, rest) = buf.split_at(HEADER_SIZE);
|
||||
match codec::u24::decode(&header[1..]) {
|
||||
Some(len) if len.0 > MAX_HANDSHAKE_SIZE => Err(JoinerError::Decode),
|
||||
Some(len) if rest.get(..len.into()).is_some() => Ok(Some(HEADER_SIZE + usize::from(len))),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum JoinerError {
|
||||
Unwanted(PlainMessage),
|
||||
Decode,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::HandshakeJoiner;
|
||||
use crate::enums::ProtocolVersion;
|
||||
use crate::msgs::base::Payload;
|
||||
use crate::msgs::codec::Codec;
|
||||
use crate::msgs::enums::{ContentType, HandshakeType};
|
||||
use crate::msgs::handshake::{HandshakeMessagePayload, HandshakePayload};
|
||||
use crate::msgs::message::{Message, MessagePayload, PlainMessage};
|
||||
|
||||
#[test]
|
||||
fn want() {
|
||||
let mut hj = HandshakeJoiner::new();
|
||||
let wanted = PlainMessage {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"\x00\x00\x00\x00".to_vec()),
|
||||
};
|
||||
|
||||
let unwanted = PlainMessage {
|
||||
typ: ContentType::Alert,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"ponytown".to_vec()),
|
||||
};
|
||||
|
||||
hj.push(wanted).unwrap();
|
||||
hj.push(unwanted).unwrap_err();
|
||||
}
|
||||
|
||||
fn pop_eq(expect: &PlainMessage, hj: &mut HandshakeJoiner) {
|
||||
let got = hj.pop().unwrap().unwrap();
|
||||
assert_eq!(got.payload.content_type(), expect.typ);
|
||||
assert_eq!(got.version, expect.version);
|
||||
|
||||
let (mut left, mut right) = (Vec::new(), Vec::new());
|
||||
got.payload.encode(&mut left);
|
||||
expect.payload.encode(&mut right);
|
||||
|
||||
assert_eq!(left, right);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split() {
|
||||
// Check we split two handshake messages within one PDU.
|
||||
let mut hj = HandshakeJoiner::new();
|
||||
|
||||
// two HelloRequests
|
||||
assert!(hj
|
||||
.push(PlainMessage {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"\x00\x00\x00\x00\x00\x00\x00\x00".to_vec()),
|
||||
})
|
||||
.unwrap());
|
||||
|
||||
let expect = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::HelloRequest,
|
||||
payload: HandshakePayload::HelloRequest,
|
||||
}),
|
||||
}
|
||||
.into();
|
||||
|
||||
pop_eq(&expect, &mut hj);
|
||||
pop_eq(&expect, &mut hj);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broken() {
|
||||
// Check obvious crap payloads are reported as errors, not panics.
|
||||
let mut hj = HandshakeJoiner::new();
|
||||
|
||||
// short ClientHello
|
||||
hj.push(PlainMessage {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"\x01\x00\x00\x02\xff\xff".to_vec()),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
hj.pop().unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn join() {
|
||||
// Check we join one handshake message split over two PDUs.
|
||||
let mut hj = HandshakeJoiner::new();
|
||||
|
||||
// Introduce Finished of 16 bytes, providing 4.
|
||||
hj.push(PlainMessage {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"\x14\x00\x00\x10\x00\x01\x02\x03\x04".to_vec()),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// 11 more bytes.
|
||||
assert!(!hj
|
||||
.push(PlainMessage {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e".to_vec()),
|
||||
})
|
||||
.unwrap());
|
||||
|
||||
// Final 1 byte.
|
||||
assert!(hj
|
||||
.push(PlainMessage {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"\x0f".to_vec()),
|
||||
})
|
||||
.unwrap());
|
||||
|
||||
let payload = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f".to_vec();
|
||||
let expect = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::Finished,
|
||||
payload: HandshakePayload::Finished(Payload::new(payload)),
|
||||
}),
|
||||
}
|
||||
.into();
|
||||
|
||||
pop_eq(&expect, &mut hj);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rejects_giant_certs() {
|
||||
let mut hj = HandshakeJoiner::new();
|
||||
hj.push(PlainMessage {
|
||||
typ: ContentType::Handshake,
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: Payload::new(b"\x0b\x01\x00\x04\x01\x00\x01\x00\xff\xfe".to_vec()),
|
||||
})
|
||||
.unwrap_err();
|
||||
}
|
||||
}
|
||||
88
third_party/rustls-fork-shadow-tls/src/msgs/macros.rs
vendored
Normal file
88
third_party/rustls-fork-shadow-tls/src/msgs/macros.rs
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/// A macro which defines an enum type.
|
||||
macro_rules! enum_builder {
|
||||
(
|
||||
$(#[$comment:meta])*
|
||||
@U8
|
||||
EnumName: $enum_name: ident;
|
||||
EnumVal { $( $enum_var: ident => $enum_val: expr ),* }
|
||||
) => {
|
||||
$(#[$comment])*
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum $enum_name {
|
||||
$( $enum_var),*
|
||||
,Unknown(u8)
|
||||
}
|
||||
impl $enum_name {
|
||||
pub fn get_u8(&self) -> u8 {
|
||||
let x = self.clone();
|
||||
match x {
|
||||
$( $enum_name::$enum_var => $enum_val),*
|
||||
,$enum_name::Unknown(x) => x
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Codec for $enum_name {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
self.get_u8().encode(bytes);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
u8::read(r).map($enum_name::from)
|
||||
}
|
||||
}
|
||||
impl From<u8> for $enum_name {
|
||||
fn from(x: u8) -> Self {
|
||||
match x {
|
||||
$($enum_val => $enum_name::$enum_var),*
|
||||
, x => $enum_name::Unknown(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[$comment:meta])*
|
||||
@U16
|
||||
EnumName: $enum_name: ident;
|
||||
EnumVal { $( $enum_var: ident => $enum_val: expr ),* }
|
||||
) => {
|
||||
$(#[$comment])*
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum $enum_name {
|
||||
$( $enum_var),*
|
||||
,Unknown(u16)
|
||||
}
|
||||
impl $enum_name {
|
||||
pub fn get_u16(&self) -> u16 {
|
||||
let x = self.clone();
|
||||
match x {
|
||||
$( $enum_name::$enum_var => $enum_val),*
|
||||
,$enum_name::Unknown(x) => x
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
$( $enum_name::$enum_var => Some(stringify!($enum_var))),*
|
||||
,$enum_name::Unknown(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Codec for $enum_name {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
self.get_u16().encode(bytes);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
u16::read(r).map($enum_name::from)
|
||||
}
|
||||
}
|
||||
impl From<u16> for $enum_name {
|
||||
fn from(x: u16) -> Self {
|
||||
match x {
|
||||
$($enum_val => $enum_name::$enum_var),*
|
||||
, x => $enum_name::Unknown(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
292
third_party/rustls-fork-shadow-tls/src/msgs/message.rs
vendored
Normal file
292
third_party/rustls-fork-shadow-tls/src/msgs/message.rs
vendored
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
use crate::enums::ProtocolVersion;
|
||||
use crate::error::Error;
|
||||
use crate::msgs::alert::AlertMessagePayload;
|
||||
use crate::msgs::base::Payload;
|
||||
use crate::msgs::ccs::ChangeCipherSpecPayload;
|
||||
use crate::msgs::codec::{Codec, Reader};
|
||||
use crate::msgs::enums::{AlertDescription, AlertLevel, ContentType, HandshakeType};
|
||||
use crate::msgs::handshake::HandshakeMessagePayload;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MessagePayload {
|
||||
Alert(AlertMessagePayload),
|
||||
Handshake {
|
||||
parsed: HandshakeMessagePayload,
|
||||
encoded: Payload,
|
||||
},
|
||||
ChangeCipherSpec(ChangeCipherSpecPayload),
|
||||
ApplicationData(Payload),
|
||||
}
|
||||
|
||||
impl MessagePayload {
|
||||
pub fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
match self {
|
||||
Self::Alert(x) => x.encode(bytes),
|
||||
Self::Handshake { encoded, .. } => bytes.extend(&encoded.0),
|
||||
Self::ChangeCipherSpec(x) => x.encode(bytes),
|
||||
Self::ApplicationData(x) => x.encode(bytes),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handshake(parsed: HandshakeMessagePayload) -> Self {
|
||||
Self::Handshake {
|
||||
encoded: Payload::new(parsed.get_encoding()),
|
||||
parsed,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(typ: ContentType, vers: ProtocolVersion, payload: Payload) -> Result<Self, Error> {
|
||||
let mut r = Reader::init(&payload.0);
|
||||
let parsed = match typ {
|
||||
ContentType::ApplicationData => return Ok(Self::ApplicationData(payload)),
|
||||
ContentType::Alert => AlertMessagePayload::read(&mut r)
|
||||
.filter(|_| !r.any_left())
|
||||
.map(MessagePayload::Alert),
|
||||
ContentType::Handshake => HandshakeMessagePayload::read_version(&mut r, vers)
|
||||
.filter(|_| !r.any_left())
|
||||
.map(|parsed| Self::Handshake {
|
||||
parsed,
|
||||
encoded: payload,
|
||||
}),
|
||||
ContentType::ChangeCipherSpec => ChangeCipherSpecPayload::read(&mut r)
|
||||
.filter(|_| !r.any_left())
|
||||
.map(MessagePayload::ChangeCipherSpec),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
parsed.ok_or(Error::CorruptMessagePayload(typ))
|
||||
}
|
||||
|
||||
pub fn content_type(&self) -> ContentType {
|
||||
match self {
|
||||
Self::Alert(_) => ContentType::Alert,
|
||||
Self::Handshake { .. } => ContentType::Handshake,
|
||||
Self::ChangeCipherSpec(_) => ContentType::ChangeCipherSpec,
|
||||
Self::ApplicationData(_) => ContentType::ApplicationData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A TLS frame, named TLSPlaintext in the standard.
|
||||
///
|
||||
/// This type owns all memory for its interior parts. It is used to read/write from/to I/O
|
||||
/// buffers as well as for fragmenting, joining and encryption/decryption. It can be converted
|
||||
/// into a `Message` by decoding the payload.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OpaqueMessage {
|
||||
pub typ: ContentType,
|
||||
pub version: ProtocolVersion,
|
||||
pub payload: Payload,
|
||||
}
|
||||
|
||||
impl OpaqueMessage {
|
||||
/// `MessageError` allows callers to distinguish between valid prefixes (might
|
||||
/// become valid if we read more data) and invalid data.
|
||||
pub fn read(r: &mut Reader) -> Result<Self, MessageError> {
|
||||
let typ = ContentType::read(r).ok_or(MessageError::TooShortForHeader)?;
|
||||
let version = ProtocolVersion::read(r).ok_or(MessageError::TooShortForHeader)?;
|
||||
let len = u16::read(r).ok_or(MessageError::TooShortForHeader)?;
|
||||
|
||||
// Reject undersize messages
|
||||
// implemented per section 5.1 of RFC8446 (TLSv1.3)
|
||||
// per section 6.2.1 of RFC5246 (TLSv1.2)
|
||||
if typ != ContentType::ApplicationData && len == 0 {
|
||||
return Err(MessageError::IllegalLength);
|
||||
}
|
||||
|
||||
// Reject oversize messages
|
||||
if len >= Self::MAX_PAYLOAD {
|
||||
return Err(MessageError::IllegalLength);
|
||||
}
|
||||
|
||||
// Don't accept any new content-types.
|
||||
if let ContentType::Unknown(_) = typ {
|
||||
return Err(MessageError::IllegalContentType);
|
||||
}
|
||||
|
||||
// Accept only versions 0x03XX for any XX.
|
||||
match version {
|
||||
ProtocolVersion::Unknown(ref v) if (v & 0xff00) != 0x0300 => {
|
||||
return Err(MessageError::IllegalProtocolVersion);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let mut sub = r
|
||||
.sub(len as usize)
|
||||
.ok_or(MessageError::TooShortForLength)?;
|
||||
let payload = Payload::read(&mut sub);
|
||||
|
||||
Ok(Self {
|
||||
typ,
|
||||
version,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode(self) -> Vec<u8> {
|
||||
let mut buf = Vec::new();
|
||||
self.typ.encode(&mut buf);
|
||||
self.version.encode(&mut buf);
|
||||
(self.payload.0.len() as u16).encode(&mut buf);
|
||||
self.payload.encode(&mut buf);
|
||||
buf
|
||||
}
|
||||
|
||||
/// Force conversion into a plaintext message.
|
||||
///
|
||||
/// This should only be used for messages that are known to be in plaintext. Otherwise, the
|
||||
/// `OpaqueMessage` should be decrypted into a `PlainMessage` using a `MessageDecrypter`.
|
||||
pub fn into_plain_message(self) -> PlainMessage {
|
||||
PlainMessage {
|
||||
version: self.version,
|
||||
typ: self.typ,
|
||||
payload: self.payload,
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the maximum on-the-wire size of a TLSCiphertext.
|
||||
/// That's 2^14 payload bytes, a header, and a 2KB allowance
|
||||
/// for ciphertext overheads.
|
||||
const MAX_PAYLOAD: u16 = 16384 + 2048;
|
||||
|
||||
/// Content type, version and size.
|
||||
const HEADER_SIZE: u16 = 1 + 2 + 2;
|
||||
|
||||
/// Maximum on-wire message size.
|
||||
pub const MAX_WIRE_SIZE: usize = (Self::MAX_PAYLOAD + Self::HEADER_SIZE) as usize;
|
||||
}
|
||||
|
||||
impl From<Message> for PlainMessage {
|
||||
fn from(msg: Message) -> Self {
|
||||
let typ = msg.payload.content_type();
|
||||
let payload = match msg.payload {
|
||||
MessagePayload::ApplicationData(payload) => payload,
|
||||
_ => {
|
||||
let mut buf = Vec::new();
|
||||
msg.payload.encode(&mut buf);
|
||||
Payload(buf)
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
typ,
|
||||
version: msg.version,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A decrypted TLS frame
|
||||
///
|
||||
/// This type owns all memory for its interior parts. It can be decrypted from an OpaqueMessage
|
||||
/// or encrypted into an OpaqueMessage, and it is also used for joining and fragmenting.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlainMessage {
|
||||
pub typ: ContentType,
|
||||
pub version: ProtocolVersion,
|
||||
pub payload: Payload,
|
||||
}
|
||||
|
||||
impl PlainMessage {
|
||||
pub fn into_unencrypted_opaque(self) -> OpaqueMessage {
|
||||
OpaqueMessage {
|
||||
version: self.version,
|
||||
typ: self.typ,
|
||||
payload: self.payload,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> BorrowedPlainMessage<'_> {
|
||||
BorrowedPlainMessage {
|
||||
version: self.version,
|
||||
typ: self.typ,
|
||||
payload: &self.payload.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A message with decoded payload
|
||||
#[derive(Debug)]
|
||||
pub struct Message {
|
||||
pub version: ProtocolVersion,
|
||||
pub payload: MessagePayload,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub fn is_handshake_type(&self, hstyp: HandshakeType) -> bool {
|
||||
// Bit of a layering violation, but OK.
|
||||
if let MessagePayload::Handshake { parsed, .. } = &self.payload {
|
||||
parsed.typ == hstyp
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_alert(level: AlertLevel, desc: AlertDescription) -> Self {
|
||||
Self {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::Alert(AlertMessagePayload {
|
||||
level,
|
||||
description: desc,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_key_update_notify() -> Self {
|
||||
Self {
|
||||
version: ProtocolVersion::TLSv1_3,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload::build_key_update_notify()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a plaintext message into a well-typed [`Message`].
|
||||
///
|
||||
/// A [`PlainMessage`] must contain plaintext content. Encrypted content should be stored in an
|
||||
/// [`OpaqueMessage`] and decrypted before being stored into a [`PlainMessage`].
|
||||
impl TryFrom<PlainMessage> for Message {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(plain: PlainMessage) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
version: plain.version,
|
||||
payload: MessagePayload::new(plain.typ, plain.version, plain.payload)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A TLS frame, named TLSPlaintext in the standard.
|
||||
///
|
||||
/// This type differs from `OpaqueMessage` because it borrows
|
||||
/// its payload. You can make a `OpaqueMessage` from an
|
||||
/// `BorrowMessage`, but this involves a copy.
|
||||
///
|
||||
/// This type also cannot decode its internals and
|
||||
/// cannot be read/encoded; only `OpaqueMessage` can do that.
|
||||
pub struct BorrowedPlainMessage<'a> {
|
||||
pub typ: ContentType,
|
||||
pub version: ProtocolVersion,
|
||||
pub payload: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> BorrowedPlainMessage<'a> {
|
||||
pub fn to_unencrypted_opaque(&self) -> OpaqueMessage {
|
||||
OpaqueMessage {
|
||||
version: self.version,
|
||||
typ: self.typ,
|
||||
payload: Payload(self.payload.to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MessageError {
|
||||
TooShortForHeader,
|
||||
TooShortForLength,
|
||||
IllegalLength,
|
||||
IllegalContentType,
|
||||
IllegalProtocolVersion,
|
||||
}
|
||||
113
third_party/rustls-fork-shadow-tls/src/msgs/message_test.rs
vendored
Normal file
113
third_party/rustls-fork-shadow-tls/src/msgs/message_test.rs
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
use crate::msgs::base::{PayloadU16, PayloadU24, PayloadU8};
|
||||
|
||||
use super::base::Payload;
|
||||
use super::codec::Reader;
|
||||
use super::enums::{AlertDescription, AlertLevel, HandshakeType};
|
||||
use super::message::{Message, OpaqueMessage, PlainMessage};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[test]
|
||||
fn test_read_fuzz_corpus() {
|
||||
fn corpus_dir() -> PathBuf {
|
||||
let from_subcrate = Path::new("../fuzz/corpus/message");
|
||||
let from_root = Path::new("fuzz/corpus/message");
|
||||
|
||||
if from_root.is_dir() {
|
||||
from_root.to_path_buf()
|
||||
} else {
|
||||
from_subcrate.to_path_buf()
|
||||
}
|
||||
}
|
||||
|
||||
for file in fs::read_dir(corpus_dir()).unwrap() {
|
||||
let mut f = fs::File::open(file.unwrap().path()).unwrap();
|
||||
let mut bytes = Vec::new();
|
||||
f.read_to_end(&mut bytes).unwrap();
|
||||
|
||||
let mut rd = Reader::init(&bytes);
|
||||
let msg = OpaqueMessage::read(&mut rd)
|
||||
.unwrap()
|
||||
.into_plain_message();
|
||||
println!("{:?}", msg);
|
||||
|
||||
let msg = match Message::try_from(msg) {
|
||||
Ok(msg) => msg,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let enc = PlainMessage::from(msg)
|
||||
.into_unencrypted_opaque()
|
||||
.encode();
|
||||
assert_eq!(bytes.to_vec(), enc);
|
||||
assert_eq!(bytes[..rd.used()].to_vec(), enc);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_read_safari_client_hello() {
|
||||
let _ = env_logger::Builder::new()
|
||||
.filter(None, log::LevelFilter::Trace)
|
||||
.try_init();
|
||||
|
||||
let bytes = b"\
|
||||
\x16\x03\x01\x00\xeb\x01\x00\x00\xe7\x03\x03\xb6\x1f\xe4\x3a\x55\
|
||||
\x90\x3e\xc0\x28\x9c\x12\xe0\x5c\x84\xea\x90\x1b\xfb\x11\xfc\xbd\
|
||||
\x25\x55\xda\x9f\x51\x93\x1b\x8d\x92\x66\xfd\x00\x00\x2e\xc0\x2c\
|
||||
\xc0\x2b\xc0\x24\xc0\x23\xc0\x0a\xc0\x09\xcc\xa9\xc0\x30\xc0\x2f\
|
||||
\xc0\x28\xc0\x27\xc0\x14\xc0\x13\xcc\xa8\x00\x9d\x00\x9c\x00\x3d\
|
||||
\x00\x3c\x00\x35\x00\x2f\xc0\x08\xc0\x12\x00\x0a\x01\x00\x00\x90\
|
||||
\xff\x01\x00\x01\x00\x00\x00\x00\x0e\x00\x0c\x00\x00\x09\x31\x32\
|
||||
\x37\x2e\x30\x2e\x30\x2e\x31\x00\x17\x00\x00\x00\x0d\x00\x18\x00\
|
||||
\x16\x04\x03\x08\x04\x04\x01\x05\x03\x02\x03\x08\x05\x08\x05\x05\
|
||||
\x01\x08\x06\x06\x01\x02\x01\x00\x05\x00\x05\x01\x00\x00\x00\x00\
|
||||
\x33\x74\x00\x00\x00\x12\x00\x00\x00\x10\x00\x30\x00\x2e\x02\x68\
|
||||
\x32\x05\x68\x32\x2d\x31\x36\x05\x68\x32\x2d\x31\x35\x05\x68\x32\
|
||||
\x2d\x31\x34\x08\x73\x70\x64\x79\x2f\x33\x2e\x31\x06\x73\x70\x64\
|
||||
\x79\x2f\x33\x08\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x0b\x00\x02\
|
||||
\x01\x00\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19";
|
||||
let mut rd = Reader::init(bytes);
|
||||
let m = OpaqueMessage::read(&mut rd).unwrap();
|
||||
println!("m = {:?}", m);
|
||||
assert!(Message::try_from(m.into_plain_message()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alert_is_not_handshake() {
|
||||
let m = Message::build_alert(AlertLevel::Fatal, AlertDescription::DecodeError);
|
||||
assert!(!m.is_handshake_type(HandshakeType::ClientHello));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alert_is_not_opaque() {
|
||||
let m = Message::build_alert(AlertLevel::Fatal, AlertDescription::DecodeError);
|
||||
assert!(Message::try_from(m).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct_all_types() {
|
||||
let samples = [
|
||||
&b"\x14\x03\x04\x00\x01\x01"[..],
|
||||
&b"\x15\x03\x04\x00\x02\x01\x16"[..],
|
||||
&b"\x16\x03\x04\x00\x05\x18\x00\x00\x01\x00"[..],
|
||||
&b"\x17\x03\x04\x00\x04\x11\x22\x33\x44"[..],
|
||||
&b"\x18\x03\x04\x00\x04\x11\x22\x33\x44"[..],
|
||||
];
|
||||
for &bytes in samples.iter() {
|
||||
let m = OpaqueMessage::read(&mut Reader::init(bytes)).unwrap();
|
||||
println!("m = {:?}", m);
|
||||
let m = Message::try_from(m.into_plain_message());
|
||||
println!("m' = {:?}", m);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_payload() {
|
||||
assert_eq!("01020304", format!("{:?}", Payload(vec![1, 2, 3, 4])));
|
||||
assert_eq!("01020304", format!("{:?}", PayloadU8(vec![1, 2, 3, 4])));
|
||||
assert_eq!("01020304", format!("{:?}", PayloadU16(vec![1, 2, 3, 4])));
|
||||
assert_eq!("01020304", format!("{:?}", PayloadU24(vec![1, 2, 3, 4])));
|
||||
}
|
||||
51
third_party/rustls-fork-shadow-tls/src/msgs/mod.rs
vendored
Normal file
51
third_party/rustls-fork-shadow-tls/src/msgs/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod alert;
|
||||
pub mod base;
|
||||
pub mod ccs;
|
||||
pub mod codec;
|
||||
pub mod deframer;
|
||||
pub mod enums;
|
||||
pub mod fragmenter;
|
||||
pub mod handshake;
|
||||
pub mod hsjoiner;
|
||||
pub mod message;
|
||||
pub mod persist;
|
||||
|
||||
#[cfg(test)]
|
||||
mod handshake_test;
|
||||
|
||||
#[cfg(test)]
|
||||
mod persist_test;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod enums_test;
|
||||
|
||||
#[cfg(test)]
|
||||
mod message_test;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn smoketest() {
|
||||
use super::codec::Reader;
|
||||
use super::message::{Message, OpaqueMessage};
|
||||
let bytes = include_bytes!("handshake-test.1.bin");
|
||||
let mut r = Reader::init(bytes);
|
||||
|
||||
while r.any_left() {
|
||||
let m = OpaqueMessage::read(&mut r).unwrap();
|
||||
|
||||
let out = m.clone().encode();
|
||||
assert!(!out.is_empty());
|
||||
|
||||
Message::try_from(m.into_plain_message()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
543
third_party/rustls-fork-shadow-tls/src/msgs/persist.rs
vendored
Normal file
543
third_party/rustls-fork-shadow-tls/src/msgs/persist.rs
vendored
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
use crate::client::ServerName;
|
||||
use crate::enums::{CipherSuite, ProtocolVersion};
|
||||
use crate::key;
|
||||
use crate::msgs::base::{PayloadU16, PayloadU8};
|
||||
use crate::msgs::codec::{Codec, Reader};
|
||||
use crate::msgs::handshake::CertificatePayload;
|
||||
use crate::msgs::handshake::SessionID;
|
||||
use crate::suites::SupportedCipherSuite;
|
||||
use crate::ticketer::TimeBase;
|
||||
#[cfg(feature = "tls12")]
|
||||
use crate::tls12::Tls12CipherSuite;
|
||||
use crate::tls13::Tls13CipherSuite;
|
||||
|
||||
use std::cmp;
|
||||
#[cfg(feature = "tls12")]
|
||||
use std::mem;
|
||||
|
||||
// These are the keys and values we store in session storage.
|
||||
|
||||
// --- Client types ---
|
||||
/// Keys for session resumption and tickets.
|
||||
/// Matching value is a `ClientSessionValue`.
|
||||
#[derive(Debug)]
|
||||
pub struct ClientSessionKey {
|
||||
kind: &'static [u8],
|
||||
name: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Codec for ClientSessionKey {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
bytes.extend_from_slice(self.kind);
|
||||
bytes.extend_from_slice(&self.name);
|
||||
}
|
||||
|
||||
// Don't need to read these.
|
||||
fn read(_r: &mut Reader) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientSessionKey {
|
||||
pub fn session_for_server_name(server_name: &ServerName) -> Self {
|
||||
Self {
|
||||
kind: b"session",
|
||||
name: server_name.encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hint_for_server_name(server_name: &ServerName) -> Self {
|
||||
Self {
|
||||
kind: b"kx-hint",
|
||||
name: server_name.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ClientSessionValue {
|
||||
Tls13(Tls13ClientSessionValue),
|
||||
#[cfg(feature = "tls12")]
|
||||
Tls12(Tls12ClientSessionValue),
|
||||
}
|
||||
|
||||
impl ClientSessionValue {
|
||||
pub fn read(
|
||||
reader: &mut Reader<'_>,
|
||||
suite: CipherSuite,
|
||||
supported: &[SupportedCipherSuite],
|
||||
) -> Option<Self> {
|
||||
match supported
|
||||
.iter()
|
||||
.find(|s| s.suite() == suite)?
|
||||
{
|
||||
SupportedCipherSuite::Tls13(inner) => {
|
||||
Tls13ClientSessionValue::read(inner, reader).map(ClientSessionValue::Tls13)
|
||||
}
|
||||
#[cfg(feature = "tls12")]
|
||||
SupportedCipherSuite::Tls12(inner) => {
|
||||
Tls12ClientSessionValue::read(inner, reader).map(ClientSessionValue::Tls12)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn common(&self) -> &ClientSessionCommon {
|
||||
match self {
|
||||
Self::Tls13(inner) => &inner.common,
|
||||
#[cfg(feature = "tls12")]
|
||||
Self::Tls12(inner) => &inner.common,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tls13ClientSessionValue> for ClientSessionValue {
|
||||
fn from(v: Tls13ClientSessionValue) -> Self {
|
||||
Self::Tls13(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
impl From<Tls12ClientSessionValue> for ClientSessionValue {
|
||||
fn from(v: Tls12ClientSessionValue) -> Self {
|
||||
Self::Tls12(v)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Retrieved<T> {
|
||||
pub value: T,
|
||||
retrieved_at: TimeBase,
|
||||
}
|
||||
|
||||
impl<T> Retrieved<T> {
|
||||
pub fn new(value: T, retrieved_at: TimeBase) -> Self {
|
||||
Self {
|
||||
value,
|
||||
retrieved_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Retrieved<&Tls13ClientSessionValue> {
|
||||
pub fn obfuscated_ticket_age(&self) -> u32 {
|
||||
let age_secs = self
|
||||
.retrieved_at
|
||||
.as_secs()
|
||||
.saturating_sub(self.value.common.epoch);
|
||||
let age_millis = age_secs as u32 * 1000;
|
||||
age_millis.wrapping_add(self.value.age_add)
|
||||
}
|
||||
}
|
||||
|
||||
impl Retrieved<ClientSessionValue> {
|
||||
pub fn tls13(&self) -> Option<Retrieved<&Tls13ClientSessionValue>> {
|
||||
match &self.value {
|
||||
ClientSessionValue::Tls13(value) => Some(Retrieved::new(value, self.retrieved_at)),
|
||||
#[cfg(feature = "tls12")]
|
||||
ClientSessionValue::Tls12(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_expired(&self) -> bool {
|
||||
let common = self.value.common();
|
||||
common.lifetime_secs != 0
|
||||
&& common
|
||||
.epoch
|
||||
.saturating_add(u64::from(common.lifetime_secs))
|
||||
< self.retrieved_at.as_secs()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for Retrieved<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Tls13ClientSessionValue {
|
||||
suite: &'static Tls13CipherSuite,
|
||||
age_add: u32,
|
||||
max_early_data_size: u32,
|
||||
pub common: ClientSessionCommon,
|
||||
}
|
||||
|
||||
impl Tls13ClientSessionValue {
|
||||
pub fn new(
|
||||
suite: &'static Tls13CipherSuite,
|
||||
ticket: Vec<u8>,
|
||||
secret: Vec<u8>,
|
||||
server_cert_chain: Vec<key::Certificate>,
|
||||
time_now: TimeBase,
|
||||
lifetime_secs: u32,
|
||||
age_add: u32,
|
||||
max_early_data_size: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
suite,
|
||||
age_add,
|
||||
max_early_data_size,
|
||||
common: ClientSessionCommon::new(
|
||||
ticket,
|
||||
secret,
|
||||
time_now,
|
||||
lifetime_secs,
|
||||
server_cert_chain,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Codec::read()`] with an extra `suite` argument.
|
||||
///
|
||||
/// We decode the `suite` argument separately because it allows us to
|
||||
/// decide whether we're decoding an 1.2 or 1.3 session value.
|
||||
pub fn read(suite: &'static Tls13CipherSuite, r: &mut Reader) -> Option<Self> {
|
||||
Some(Self {
|
||||
suite,
|
||||
age_add: u32::read(r)?,
|
||||
max_early_data_size: u32::read(r)?,
|
||||
common: ClientSessionCommon::read(r)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Inherent implementation of the [`Codec::get_encoding()`] method.
|
||||
///
|
||||
/// (See `read()` for why this is inherent here.)
|
||||
pub fn get_encoding(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(16);
|
||||
self.suite
|
||||
.common
|
||||
.suite
|
||||
.encode(&mut bytes);
|
||||
self.age_add.encode(&mut bytes);
|
||||
self.max_early_data_size
|
||||
.encode(&mut bytes);
|
||||
self.common.encode(&mut bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn max_early_data_size(&self) -> u32 {
|
||||
self.max_early_data_size
|
||||
}
|
||||
|
||||
pub fn suite(&self) -> &'static Tls13CipherSuite {
|
||||
self.suite
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Tls13ClientSessionValue {
|
||||
type Target = ClientSessionCommon;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.common
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
#[derive(Debug)]
|
||||
pub struct Tls12ClientSessionValue {
|
||||
suite: &'static Tls12CipherSuite,
|
||||
pub session_id: SessionID,
|
||||
extended_ms: bool,
|
||||
pub common: ClientSessionCommon,
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
impl Tls12ClientSessionValue {
|
||||
pub fn new(
|
||||
suite: &'static Tls12CipherSuite,
|
||||
session_id: SessionID,
|
||||
ticket: Vec<u8>,
|
||||
master_secret: Vec<u8>,
|
||||
server_cert_chain: Vec<key::Certificate>,
|
||||
time_now: TimeBase,
|
||||
lifetime_secs: u32,
|
||||
extended_ms: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
suite,
|
||||
session_id,
|
||||
extended_ms,
|
||||
common: ClientSessionCommon::new(
|
||||
ticket,
|
||||
master_secret,
|
||||
time_now,
|
||||
lifetime_secs,
|
||||
server_cert_chain,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Codec::read()`] with an extra `suite` argument.
|
||||
///
|
||||
/// We decode the `suite` argument separately because it allows us to
|
||||
/// decide whether we're decoding an 1.2 or 1.3 session value.
|
||||
fn read(suite: &'static Tls12CipherSuite, r: &mut Reader) -> Option<Self> {
|
||||
Some(Self {
|
||||
suite,
|
||||
session_id: SessionID::read(r)?,
|
||||
extended_ms: u8::read(r)? == 1,
|
||||
common: ClientSessionCommon::read(r)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Inherent implementation of the [`Codec::get_encoding()`] method.
|
||||
///
|
||||
/// (See `read()` for why this is inherent here.)
|
||||
pub fn get_encoding(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(16);
|
||||
self.suite
|
||||
.common
|
||||
.suite
|
||||
.encode(&mut bytes);
|
||||
self.session_id.encode(&mut bytes);
|
||||
(u8::from(self.extended_ms)).encode(&mut bytes);
|
||||
self.common.encode(&mut bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn take_ticket(&mut self) -> Vec<u8> {
|
||||
mem::take(&mut self.common.ticket.0)
|
||||
}
|
||||
|
||||
pub fn extended_ms(&self) -> bool {
|
||||
self.extended_ms
|
||||
}
|
||||
|
||||
pub fn suite(&self) -> &'static Tls12CipherSuite {
|
||||
self.suite
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
impl std::ops::Deref for Tls12ClientSessionValue {
|
||||
type Target = ClientSessionCommon;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.common
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClientSessionCommon {
|
||||
ticket: PayloadU16,
|
||||
secret: PayloadU8,
|
||||
epoch: u64,
|
||||
lifetime_secs: u32,
|
||||
server_cert_chain: CertificatePayload,
|
||||
}
|
||||
|
||||
impl ClientSessionCommon {
|
||||
fn new(
|
||||
ticket: Vec<u8>,
|
||||
secret: Vec<u8>,
|
||||
time_now: TimeBase,
|
||||
lifetime_secs: u32,
|
||||
server_cert_chain: Vec<key::Certificate>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ticket: PayloadU16(ticket),
|
||||
secret: PayloadU8(secret),
|
||||
epoch: time_now.as_secs(),
|
||||
lifetime_secs: cmp::min(lifetime_secs, MAX_TICKET_LIFETIME),
|
||||
server_cert_chain,
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Codec::read()`] is inherent here to avoid leaking the [`Codec`]
|
||||
/// implementation through [`Deref`] implementations on
|
||||
/// [`Tls12ClientSessionValue`] and [`Tls13ClientSessionValue`].
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
Some(Self {
|
||||
ticket: PayloadU16::read(r)?,
|
||||
secret: PayloadU8::read(r)?,
|
||||
epoch: u64::read(r)?,
|
||||
lifetime_secs: u32::read(r)?,
|
||||
server_cert_chain: CertificatePayload::read(r)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// [`Codec::encode()`] is inherent here to avoid leaking the [`Codec`]
|
||||
/// implementation through [`Deref`] implementations on
|
||||
/// [`Tls12ClientSessionValue`] and [`Tls13ClientSessionValue`].
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
self.ticket.encode(bytes);
|
||||
self.secret.encode(bytes);
|
||||
self.epoch.encode(bytes);
|
||||
self.lifetime_secs.encode(bytes);
|
||||
self.server_cert_chain.encode(bytes);
|
||||
}
|
||||
|
||||
pub fn server_cert_chain(&self) -> &[key::Certificate] {
|
||||
self.server_cert_chain.as_ref()
|
||||
}
|
||||
|
||||
pub fn secret(&self) -> &[u8] {
|
||||
self.secret.0.as_ref()
|
||||
}
|
||||
|
||||
pub fn ticket(&self) -> &[u8] {
|
||||
self.ticket.0.as_ref()
|
||||
}
|
||||
|
||||
/// Test only: wind back epoch by delta seconds.
|
||||
pub fn rewind_epoch(&mut self, delta: u32) {
|
||||
self.epoch -= delta as u64;
|
||||
}
|
||||
}
|
||||
|
||||
static MAX_TICKET_LIFETIME: u32 = 7 * 24 * 60 * 60;
|
||||
|
||||
/// This is the maximum allowed skew between server and client clocks, over
|
||||
/// the maximum ticket lifetime period. This encompasses TCP retransmission
|
||||
/// times in case packet loss occurs when the client sends the ClientHello
|
||||
/// or receives the NewSessionTicket, _and_ actual clock skew over this period.
|
||||
static MAX_FRESHNESS_SKEW_MS: u32 = 60 * 1000;
|
||||
|
||||
// --- Server types ---
|
||||
pub type ServerSessionKey = SessionID;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServerSessionValue {
|
||||
pub sni: Option<webpki::DnsName>,
|
||||
pub version: ProtocolVersion,
|
||||
pub cipher_suite: CipherSuite,
|
||||
pub master_secret: PayloadU8,
|
||||
pub extended_ms: bool,
|
||||
pub client_cert_chain: Option<CertificatePayload>,
|
||||
pub alpn: Option<PayloadU8>,
|
||||
pub application_data: PayloadU16,
|
||||
pub creation_time_sec: u64,
|
||||
pub age_obfuscation_offset: u32,
|
||||
freshness: Option<bool>,
|
||||
}
|
||||
|
||||
impl Codec for ServerSessionValue {
|
||||
fn encode(&self, bytes: &mut Vec<u8>) {
|
||||
if let Some(ref sni) = self.sni {
|
||||
1u8.encode(bytes);
|
||||
let sni_bytes: &str = sni.as_ref().into();
|
||||
PayloadU8::new(Vec::from(sni_bytes)).encode(bytes);
|
||||
} else {
|
||||
0u8.encode(bytes);
|
||||
}
|
||||
self.version.encode(bytes);
|
||||
self.cipher_suite.encode(bytes);
|
||||
self.master_secret.encode(bytes);
|
||||
(u8::from(self.extended_ms)).encode(bytes);
|
||||
if let Some(ref chain) = self.client_cert_chain {
|
||||
1u8.encode(bytes);
|
||||
chain.encode(bytes);
|
||||
} else {
|
||||
0u8.encode(bytes);
|
||||
}
|
||||
if let Some(ref alpn) = self.alpn {
|
||||
1u8.encode(bytes);
|
||||
alpn.encode(bytes);
|
||||
} else {
|
||||
0u8.encode(bytes);
|
||||
}
|
||||
self.application_data.encode(bytes);
|
||||
self.creation_time_sec.encode(bytes);
|
||||
self.age_obfuscation_offset
|
||||
.encode(bytes);
|
||||
}
|
||||
|
||||
fn read(r: &mut Reader) -> Option<Self> {
|
||||
let has_sni = u8::read(r)?;
|
||||
let sni = if has_sni == 1 {
|
||||
let dns_name = PayloadU8::read(r)?;
|
||||
let dns_name = webpki::DnsNameRef::try_from_ascii(&dns_name.0).ok()?;
|
||||
Some(dns_name.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let v = ProtocolVersion::read(r)?;
|
||||
let cs = CipherSuite::read(r)?;
|
||||
let ms = PayloadU8::read(r)?;
|
||||
let ems = u8::read(r)?;
|
||||
let has_ccert = u8::read(r)? == 1;
|
||||
let ccert = if has_ccert {
|
||||
Some(CertificatePayload::read(r)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let has_alpn = u8::read(r)? == 1;
|
||||
let alpn = if has_alpn {
|
||||
Some(PayloadU8::read(r)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let application_data = PayloadU16::read(r)?;
|
||||
let creation_time_sec = u64::read(r)?;
|
||||
let age_obfuscation_offset = u32::read(r)?;
|
||||
|
||||
Some(Self {
|
||||
sni,
|
||||
version: v,
|
||||
cipher_suite: cs,
|
||||
master_secret: ms,
|
||||
extended_ms: ems == 1u8,
|
||||
client_cert_chain: ccert,
|
||||
alpn,
|
||||
application_data,
|
||||
creation_time_sec,
|
||||
age_obfuscation_offset,
|
||||
freshness: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerSessionValue {
|
||||
pub fn new(
|
||||
sni: Option<&webpki::DnsName>,
|
||||
v: ProtocolVersion,
|
||||
cs: CipherSuite,
|
||||
ms: Vec<u8>,
|
||||
client_cert_chain: Option<CertificatePayload>,
|
||||
alpn: Option<Vec<u8>>,
|
||||
application_data: Vec<u8>,
|
||||
creation_time: TimeBase,
|
||||
age_obfuscation_offset: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
sni: sni.cloned(),
|
||||
version: v,
|
||||
cipher_suite: cs,
|
||||
master_secret: PayloadU8::new(ms),
|
||||
extended_ms: false,
|
||||
client_cert_chain,
|
||||
alpn: alpn.map(PayloadU8::new),
|
||||
application_data: PayloadU16::new(application_data),
|
||||
creation_time_sec: creation_time.as_secs(),
|
||||
age_obfuscation_offset,
|
||||
freshness: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_extended_ms_used(&mut self) {
|
||||
self.extended_ms = true;
|
||||
}
|
||||
|
||||
pub fn set_freshness(mut self, obfuscated_client_age_ms: u32, time_now: TimeBase) -> Self {
|
||||
let client_age_ms = obfuscated_client_age_ms.wrapping_sub(self.age_obfuscation_offset);
|
||||
let server_age_ms = (time_now
|
||||
.as_secs()
|
||||
.saturating_sub(self.creation_time_sec) as u32)
|
||||
.saturating_mul(1000);
|
||||
|
||||
let age_difference = if client_age_ms < server_age_ms {
|
||||
server_age_ms - client_age_ms
|
||||
} else {
|
||||
client_age_ms - server_age_ms
|
||||
};
|
||||
|
||||
self.freshness = Some(age_difference <= MAX_FRESHNESS_SKEW_MS);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_fresh(&self) -> bool {
|
||||
self.freshness.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
78
third_party/rustls-fork-shadow-tls/src/msgs/persist_test.rs
vendored
Normal file
78
third_party/rustls-fork-shadow-tls/src/msgs/persist_test.rs
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
use super::codec::{Codec, Reader};
|
||||
use super::persist::*;
|
||||
use crate::enums::*;
|
||||
|
||||
use crate::key::Certificate;
|
||||
use crate::ticketer::TimeBase;
|
||||
use crate::tls13::TLS13_AES_128_GCM_SHA256;
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[test]
|
||||
fn clientsessionkey_is_debug() {
|
||||
let name = "hello".try_into().unwrap();
|
||||
let csk = ClientSessionKey::session_for_server_name(&name);
|
||||
println!("{:?}", csk);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clientsessionkey_cannot_be_read() {
|
||||
let bytes = [0; 1];
|
||||
let mut rd = Reader::init(&bytes);
|
||||
assert!(ClientSessionKey::read(&mut rd).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clientsessionvalue_is_debug() {
|
||||
let csv = ClientSessionValue::from(Tls13ClientSessionValue::new(
|
||||
TLS13_AES_128_GCM_SHA256
|
||||
.tls13()
|
||||
.unwrap(),
|
||||
vec![],
|
||||
vec![1, 2, 3],
|
||||
vec![Certificate(b"abc".to_vec()), Certificate(b"def".to_vec())],
|
||||
TimeBase::now().unwrap(),
|
||||
15,
|
||||
10,
|
||||
128,
|
||||
));
|
||||
println!("{:?}", csv);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serversessionvalue_is_debug() {
|
||||
let ssv = ServerSessionValue::new(
|
||||
None,
|
||||
ProtocolVersion::TLSv1_3,
|
||||
CipherSuite::TLS13_AES_128_GCM_SHA256,
|
||||
vec![1, 2, 3],
|
||||
None,
|
||||
None,
|
||||
vec![4, 5, 6],
|
||||
TimeBase::now().unwrap(),
|
||||
0x12345678,
|
||||
);
|
||||
println!("{:?}", ssv);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serversessionvalue_no_sni() {
|
||||
let bytes = [
|
||||
0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
|
||||
0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d,
|
||||
];
|
||||
let mut rd = Reader::init(&bytes);
|
||||
let ssv = ServerSessionValue::read(&mut rd).unwrap();
|
||||
assert_eq!(ssv.get_encoding(), bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serversessionvalue_with_cert() {
|
||||
let bytes = [
|
||||
0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
|
||||
0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d,
|
||||
];
|
||||
let mut rd = Reader::init(&bytes);
|
||||
let ssv = ServerSessionValue::read(&mut rd).unwrap();
|
||||
assert_eq!(ssv.get_encoding(), bytes);
|
||||
}
|
||||
620
third_party/rustls-fork-shadow-tls/src/quic.rs
vendored
Normal file
620
third_party/rustls-fork-shadow-tls/src/quic.rs
vendored
Normal file
|
|
@ -0,0 +1,620 @@
|
|||
/// This module contains optional APIs for implementing QUIC TLS.
|
||||
use crate::cipher::{Iv, IvLen};
|
||||
pub use crate::client::ClientQuicExt;
|
||||
use crate::conn::CommonState;
|
||||
use crate::error::Error;
|
||||
use crate::msgs::enums::AlertDescription;
|
||||
pub use crate::server::ServerQuicExt;
|
||||
use crate::suites::BulkAlgorithm;
|
||||
use crate::tls13::key_schedule::hkdf_expand;
|
||||
use crate::tls13::{Tls13CipherSuite, TLS13_AES_128_GCM_SHA256_INTERNAL};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use ring::{aead, hkdf};
|
||||
|
||||
/// Secrets used to encrypt/decrypt traffic
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Secrets {
|
||||
/// Secret used to encrypt packets transmitted by the client
|
||||
client: hkdf::Prk,
|
||||
/// Secret used to encrypt packets transmitted by the server
|
||||
server: hkdf::Prk,
|
||||
/// Cipher suite used with these secrets
|
||||
suite: &'static Tls13CipherSuite,
|
||||
is_client: bool,
|
||||
}
|
||||
|
||||
impl Secrets {
|
||||
pub(crate) fn new(
|
||||
client: hkdf::Prk,
|
||||
server: hkdf::Prk,
|
||||
suite: &'static Tls13CipherSuite,
|
||||
is_client: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
server,
|
||||
suite,
|
||||
is_client,
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive the next set of packet keys
|
||||
pub fn next_packet_keys(&mut self) -> PacketKeySet {
|
||||
let keys = PacketKeySet::new(self);
|
||||
self.update();
|
||||
keys
|
||||
}
|
||||
|
||||
fn update(&mut self) {
|
||||
let hkdf_alg = self.suite.hkdf_algorithm;
|
||||
self.client = hkdf_expand(&self.client, hkdf_alg, b"quic ku", &[]);
|
||||
self.server = hkdf_expand(&self.server, hkdf_alg, b"quic ku", &[]);
|
||||
}
|
||||
|
||||
fn local_remote(&self) -> (&hkdf::Prk, &hkdf::Prk) {
|
||||
if self.is_client {
|
||||
(&self.client, &self.server)
|
||||
} else {
|
||||
(&self.server, &self.client)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic methods for QUIC sessions
|
||||
pub trait QuicExt {
|
||||
/// Return the TLS-encoded transport parameters for the session's peer.
|
||||
///
|
||||
/// While the transport parameters are technically available prior to the
|
||||
/// completion of the handshake, they cannot be fully trusted until the
|
||||
/// handshake completes, and reliance on them should be minimized.
|
||||
/// However, any tampering with the parameters will cause the handshake
|
||||
/// to fail.
|
||||
fn quic_transport_parameters(&self) -> Option<&[u8]>;
|
||||
|
||||
/// Compute the keys for encrypting/decrypting 0-RTT packets, if available
|
||||
fn zero_rtt_keys(&self) -> Option<DirectionalKeys>;
|
||||
|
||||
/// Consume unencrypted TLS handshake data.
|
||||
///
|
||||
/// Handshake data obtained from separate encryption levels should be supplied in separate calls.
|
||||
fn read_hs(&mut self, plaintext: &[u8]) -> Result<(), Error>;
|
||||
|
||||
/// Emit unencrypted TLS handshake data.
|
||||
///
|
||||
/// When this returns `Some(_)`, the new keys must be used for future handshake data.
|
||||
fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<KeyChange>;
|
||||
|
||||
/// Emit the TLS description code of a fatal alert, if one has arisen.
|
||||
///
|
||||
/// Check after `read_hs` returns `Err(_)`.
|
||||
fn alert(&self) -> Option<AlertDescription>;
|
||||
}
|
||||
|
||||
/// Keys used to communicate in a single direction
|
||||
pub struct DirectionalKeys {
|
||||
/// Encrypts or decrypts a packet's headers
|
||||
pub header: HeaderProtectionKey,
|
||||
/// Encrypts or decrypts the payload of a packet
|
||||
pub packet: PacketKey,
|
||||
}
|
||||
|
||||
impl DirectionalKeys {
|
||||
pub(crate) fn new(suite: &'static Tls13CipherSuite, secret: &hkdf::Prk) -> Self {
|
||||
Self {
|
||||
header: HeaderProtectionKey::new(suite, secret),
|
||||
packet: PacketKey::new(suite, secret),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A QUIC header protection key
|
||||
pub struct HeaderProtectionKey(aead::quic::HeaderProtectionKey);
|
||||
|
||||
impl HeaderProtectionKey {
|
||||
fn new(suite: &'static Tls13CipherSuite, secret: &hkdf::Prk) -> Self {
|
||||
let alg = match suite.common.bulk {
|
||||
BulkAlgorithm::Aes128Gcm => &aead::quic::AES_128,
|
||||
BulkAlgorithm::Aes256Gcm => &aead::quic::AES_256,
|
||||
BulkAlgorithm::Chacha20Poly1305 => &aead::quic::CHACHA20,
|
||||
};
|
||||
|
||||
Self(hkdf_expand(secret, alg, b"quic hp", &[]))
|
||||
}
|
||||
|
||||
/// Adds QUIC Header Protection.
|
||||
///
|
||||
/// `sample` must contain the sample of encrypted payload; see
|
||||
/// [Header Protection Sample].
|
||||
///
|
||||
/// `first` must reference the first byte of the header, referred to as
|
||||
/// `packet[0]` in [Header Protection Application].
|
||||
///
|
||||
/// `packet_number` must reference the Packet Number field; this is
|
||||
/// `packet[pn_offset:pn_offset+pn_length]` in [Header Protection Application].
|
||||
///
|
||||
/// Returns an error without modifying anything if `sample` is not
|
||||
/// the correct length (see [Header Protection Sample] and [`Self::sample_len()`]),
|
||||
/// or `packet_number` is longer than allowed (see [Packet Number Encoding and Decoding]).
|
||||
///
|
||||
/// Otherwise, `first` and `packet_number` will have the header protection added.
|
||||
///
|
||||
/// [Header Protection Application]: https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.1
|
||||
/// [Header Protection Sample]: https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.2
|
||||
/// [Packet Number Encoding and Decoding]: https://datatracker.ietf.org/doc/html/rfc9000#section-17.1
|
||||
#[inline]
|
||||
pub fn encrypt_in_place(
|
||||
&self,
|
||||
sample: &[u8],
|
||||
first: &mut u8,
|
||||
packet_number: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
self.xor_in_place(sample, first, packet_number, false)
|
||||
}
|
||||
|
||||
/// Removes QUIC Header Protection.
|
||||
///
|
||||
/// `sample` must contain the sample of encrypted payload; see
|
||||
/// [Header Protection Sample].
|
||||
///
|
||||
/// `first` must reference the first byte of the header, referred to as
|
||||
/// `packet[0]` in [Header Protection Application].
|
||||
///
|
||||
/// `packet_number` must reference the Packet Number field; this is
|
||||
/// `packet[pn_offset:pn_offset+pn_length]` in [Header Protection Application].
|
||||
///
|
||||
/// Returns an error without modifying anything if `sample` is not
|
||||
/// the correct length (see [Header Protection Sample] and [`Self::sample_len()`]),
|
||||
/// or `packet_number` is longer than allowed (see
|
||||
/// [Packet Number Encoding and Decoding]).
|
||||
///
|
||||
/// Otherwise, `first` and `packet_number` will have the header protection removed.
|
||||
///
|
||||
/// [Header Protection Application]: https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.1
|
||||
/// [Header Protection Sample]: https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.2
|
||||
/// [Packet Number Encoding and Decoding]: https://datatracker.ietf.org/doc/html/rfc9000#section-17.1
|
||||
#[inline]
|
||||
pub fn decrypt_in_place(
|
||||
&self,
|
||||
sample: &[u8],
|
||||
first: &mut u8,
|
||||
packet_number: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
self.xor_in_place(sample, first, packet_number, true)
|
||||
}
|
||||
|
||||
fn xor_in_place(
|
||||
&self,
|
||||
sample: &[u8],
|
||||
first: &mut u8,
|
||||
packet_number: &mut [u8],
|
||||
masked: bool,
|
||||
) -> Result<(), Error> {
|
||||
// This implements [Header Protection Application] almost verbatim.
|
||||
|
||||
let mask = self
|
||||
.0
|
||||
.new_mask(sample)
|
||||
.map_err(|_| Error::General("sample of invalid length".into()))?;
|
||||
|
||||
// The `unwrap()` will not panic because `new_mask` returns a
|
||||
// non-empty result.
|
||||
let (first_mask, pn_mask) = mask.split_first().unwrap();
|
||||
|
||||
// It is OK for the `mask` to be longer than `packet_number`,
|
||||
// but a valid `packet_number` will never be longer than `mask`.
|
||||
if packet_number.len() > pn_mask.len() {
|
||||
return Err(Error::General("packet number too long".into()));
|
||||
}
|
||||
|
||||
// Infallible from this point on. Before this point, `first` and
|
||||
// `packet_number` are unchanged.
|
||||
|
||||
const LONG_HEADER_FORM: u8 = 0x80;
|
||||
let bits = match *first & LONG_HEADER_FORM == LONG_HEADER_FORM {
|
||||
true => 0x0f, // Long header: 4 bits masked
|
||||
false => 0x1f, // Short header: 5 bits masked
|
||||
};
|
||||
|
||||
let first_plain = match masked {
|
||||
// When unmasking, use the packet length bits after unmasking
|
||||
true => *first ^ (first_mask & bits),
|
||||
// When masking, use the packet length bits before masking
|
||||
false => *first,
|
||||
};
|
||||
let pn_len = (first_plain & 0x03) as usize + 1;
|
||||
|
||||
*first ^= first_mask & bits;
|
||||
for (dst, m) in packet_number
|
||||
.iter_mut()
|
||||
.zip(pn_mask)
|
||||
.take(pn_len)
|
||||
{
|
||||
*dst ^= m;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Expected sample length for the key's algorithm
|
||||
#[inline]
|
||||
pub fn sample_len(&self) -> usize {
|
||||
self.0.algorithm().sample_len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Keys to encrypt or decrypt the payload of a packet
|
||||
pub struct PacketKey {
|
||||
/// Encrypts or decrypts a packet's payload
|
||||
key: aead::LessSafeKey,
|
||||
/// Computes unique nonces for each packet
|
||||
iv: Iv,
|
||||
/// The cipher suite used for this packet key
|
||||
suite: &'static Tls13CipherSuite,
|
||||
}
|
||||
|
||||
impl PacketKey {
|
||||
fn new(suite: &'static Tls13CipherSuite, secret: &hkdf::Prk) -> Self {
|
||||
Self {
|
||||
key: aead::LessSafeKey::new(hkdf_expand(
|
||||
secret,
|
||||
suite.common.aead_algorithm,
|
||||
b"quic key",
|
||||
&[],
|
||||
)),
|
||||
iv: hkdf_expand(secret, IvLen, b"quic iv", &[]),
|
||||
suite,
|
||||
}
|
||||
}
|
||||
|
||||
/// Encrypt a QUIC packet
|
||||
///
|
||||
/// Takes a `packet_number`, used to derive the nonce; the packet `header`, which is used as
|
||||
/// the additional authenticated data; and the `payload`. The authentication tag is returned if
|
||||
/// encryption succeeds.
|
||||
///
|
||||
/// Fails iff the payload is longer than allowed by the cipher suite's AEAD algorithm.
|
||||
pub fn encrypt_in_place(
|
||||
&self,
|
||||
packet_number: u64,
|
||||
header: &[u8],
|
||||
payload: &mut [u8],
|
||||
) -> Result<Tag, Error> {
|
||||
let aad = aead::Aad::from(header);
|
||||
let nonce = nonce_for(packet_number, &self.iv);
|
||||
let tag = self
|
||||
.key
|
||||
.seal_in_place_separate_tag(nonce, aad, payload)
|
||||
.map_err(|_| Error::EncryptError)?;
|
||||
Ok(Tag(tag))
|
||||
}
|
||||
|
||||
/// Decrypt a QUIC packet
|
||||
///
|
||||
/// Takes the packet `header`, which is used as the additional authenticated data, and the
|
||||
/// `payload`, which includes the authentication tag.
|
||||
///
|
||||
/// If the return value is `Ok`, the decrypted payload can be found in `payload`, up to the
|
||||
/// length found in the return value.
|
||||
pub fn decrypt_in_place<'a>(
|
||||
&self,
|
||||
packet_number: u64,
|
||||
header: &[u8],
|
||||
payload: &'a mut [u8],
|
||||
) -> Result<&'a [u8], Error> {
|
||||
let payload_len = payload.len();
|
||||
let aad = aead::Aad::from(header);
|
||||
let nonce = nonce_for(packet_number, &self.iv);
|
||||
self.key
|
||||
.open_in_place(nonce, aad, payload)
|
||||
.map_err(|_| Error::DecryptError)?;
|
||||
|
||||
let plain_len = payload_len - self.key.algorithm().tag_len();
|
||||
Ok(&payload[..plain_len])
|
||||
}
|
||||
|
||||
/// Number of times the packet key can be used without sacrificing confidentiality
|
||||
///
|
||||
/// See <https://www.rfc-editor.org/rfc/rfc9001.html#name-confidentiality-limit>.
|
||||
#[inline]
|
||||
pub fn confidentiality_limit(&self) -> u64 {
|
||||
self.suite.confidentiality_limit
|
||||
}
|
||||
|
||||
/// Number of times the packet key can be used without sacrificing integrity
|
||||
///
|
||||
/// See <https://www.rfc-editor.org/rfc/rfc9001.html#name-integrity-limit>.
|
||||
#[inline]
|
||||
pub fn integrity_limit(&self) -> u64 {
|
||||
self.suite.integrity_limit
|
||||
}
|
||||
|
||||
/// Tag length for the underlying AEAD algorithm
|
||||
#[inline]
|
||||
pub fn tag_len(&self) -> usize {
|
||||
self.key.algorithm().tag_len()
|
||||
}
|
||||
}
|
||||
|
||||
/// AEAD tag, must be appended to encrypted cipher text
|
||||
pub struct Tag(aead::Tag);
|
||||
|
||||
impl AsRef<[u8]> for Tag {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Packet protection keys for bidirectional 1-RTT communication
|
||||
pub struct PacketKeySet {
|
||||
/// Encrypts outgoing packets
|
||||
pub local: PacketKey,
|
||||
/// Decrypts incoming packets
|
||||
pub remote: PacketKey,
|
||||
}
|
||||
|
||||
impl PacketKeySet {
|
||||
fn new(secrets: &Secrets) -> Self {
|
||||
let (local, remote) = secrets.local_remote();
|
||||
Self {
|
||||
local: PacketKey::new(secrets.suite, local),
|
||||
remote: PacketKey::new(secrets.suite, remote),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Complete set of keys used to communicate with the peer
|
||||
pub struct Keys {
|
||||
/// Encrypts outgoing packets
|
||||
pub local: DirectionalKeys,
|
||||
/// Decrypts incoming packets
|
||||
pub remote: DirectionalKeys,
|
||||
}
|
||||
|
||||
impl Keys {
|
||||
/// Construct keys for use with initial packets
|
||||
pub fn initial(version: Version, client_dst_connection_id: &[u8], is_client: bool) -> Self {
|
||||
const CLIENT_LABEL: &[u8] = b"client in";
|
||||
const SERVER_LABEL: &[u8] = b"server in";
|
||||
let salt = version.initial_salt();
|
||||
let hs_secret = hkdf::Salt::new(hkdf::HKDF_SHA256, salt).extract(client_dst_connection_id);
|
||||
|
||||
let secrets = Secrets {
|
||||
client: hkdf_expand(&hs_secret, hkdf::HKDF_SHA256, CLIENT_LABEL, &[]),
|
||||
server: hkdf_expand(&hs_secret, hkdf::HKDF_SHA256, SERVER_LABEL, &[]),
|
||||
suite: TLS13_AES_128_GCM_SHA256_INTERNAL,
|
||||
is_client,
|
||||
};
|
||||
Self::new(&secrets)
|
||||
}
|
||||
|
||||
fn new(secrets: &Secrets) -> Self {
|
||||
let (local, remote) = secrets.local_remote();
|
||||
Self {
|
||||
local: DirectionalKeys::new(secrets.suite, local),
|
||||
remote: DirectionalKeys::new(secrets.suite, remote),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn write_hs(this: &mut CommonState, buf: &mut Vec<u8>) -> Option<KeyChange> {
|
||||
while let Some((_, msg)) = this.quic.hs_queue.pop_front() {
|
||||
buf.extend_from_slice(&msg);
|
||||
if let Some(&(true, _)) = this.quic.hs_queue.front() {
|
||||
if this.quic.hs_secrets.is_some() {
|
||||
// Allow the caller to switch keys before proceeding.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(secrets) = this.quic.hs_secrets.take() {
|
||||
return Some(KeyChange::Handshake {
|
||||
keys: Keys::new(&secrets),
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(mut secrets) = this.quic.traffic_secrets.take() {
|
||||
if !this.quic.returned_traffic_keys {
|
||||
this.quic.returned_traffic_keys = true;
|
||||
let keys = Keys::new(&secrets);
|
||||
secrets.update();
|
||||
return Some(KeyChange::OneRtt {
|
||||
keys,
|
||||
next: secrets,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Key material for use in QUIC packet spaces
|
||||
///
|
||||
/// QUIC uses 4 different sets of keys (and progressive key updates for long-running connections):
|
||||
///
|
||||
/// * Initial: these can be created from [`Keys::initial()`]
|
||||
/// * 0-RTT keys: can be retrieved from [`QuicExt::zero_rtt_keys()`]
|
||||
/// * Handshake: these are returned from [`QuicExt::write_hs()`] after `ClientHello` and
|
||||
/// `ServerHello` messages have been exchanged
|
||||
/// * 1-RTT keys: these are returned from [`QuicExt::write_hs()`] after the handshake is done
|
||||
///
|
||||
/// Once the 1-RTT keys have been exchanged, either side may initiate a key update. Progressive
|
||||
/// update keys can be obtained from the [`Secrets`] returned in [`KeyChange::OneRtt`]. Note that
|
||||
/// only packet keys are updated by key updates; header protection keys remain the same.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum KeyChange {
|
||||
/// Keys for the handshake space
|
||||
Handshake {
|
||||
/// Header and packet keys for the handshake space
|
||||
keys: Keys,
|
||||
},
|
||||
/// Keys for 1-RTT data
|
||||
OneRtt {
|
||||
/// Header and packet keys for 1-RTT data
|
||||
keys: Keys,
|
||||
/// Secrets to derive updated keys from
|
||||
next: Secrets,
|
||||
},
|
||||
}
|
||||
|
||||
/// Compute the nonce to use for encrypting or decrypting `packet_number`
|
||||
fn nonce_for(packet_number: u64, iv: &Iv) -> ring::aead::Nonce {
|
||||
let mut out = [0; aead::NONCE_LEN];
|
||||
out[4..].copy_from_slice(&packet_number.to_be_bytes());
|
||||
for (out, inp) in out.iter_mut().zip(iv.0.iter()) {
|
||||
*out ^= inp;
|
||||
}
|
||||
aead::Nonce::assume_unique_for_key(out)
|
||||
}
|
||||
|
||||
/// QUIC protocol version
|
||||
///
|
||||
/// Governs version-specific behavior in the TLS layer
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Version {
|
||||
/// Draft versions 29, 30, 31 and 32
|
||||
V1Draft,
|
||||
/// First stable RFC
|
||||
V1,
|
||||
}
|
||||
|
||||
impl Version {
|
||||
fn initial_salt(self) -> &'static [u8; 20] {
|
||||
match self {
|
||||
Self::V1Draft => &[
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#section-5.2
|
||||
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61,
|
||||
0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99,
|
||||
],
|
||||
Self::V1 => &[
|
||||
// https://www.rfc-editor.org/rfc/rfc9001.html#name-initial-secrets
|
||||
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
|
||||
0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn short_packet_header_protection() {
|
||||
// https://www.rfc-editor.org/rfc/rfc9001.html#name-chacha20-poly1305-short-hea
|
||||
|
||||
const PN: u64 = 654360564;
|
||||
const SECRET: &[u8] = &[
|
||||
0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, 0x27, 0x48, 0xad,
|
||||
0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0, 0x7d, 0x60, 0x60, 0xf6, 0x88, 0xf3,
|
||||
0x0f, 0x21, 0x63, 0x2b,
|
||||
];
|
||||
|
||||
let secret = hkdf::Prk::new_less_safe(hkdf::HKDF_SHA256, SECRET);
|
||||
use crate::tls13::TLS13_CHACHA20_POLY1305_SHA256_INTERNAL;
|
||||
let hpk = HeaderProtectionKey::new(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL, &secret);
|
||||
let packet = PacketKey::new(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL, &secret);
|
||||
|
||||
const PLAIN: &[u8] = &[0x42, 0x00, 0xbf, 0xf4, 0x01];
|
||||
|
||||
let mut buf = PLAIN.to_vec();
|
||||
let (header, payload) = buf.split_at_mut(4);
|
||||
let tag = packet
|
||||
.encrypt_in_place(PN, &*header, payload)
|
||||
.unwrap();
|
||||
buf.extend(tag.as_ref());
|
||||
|
||||
let pn_offset = 1;
|
||||
let (header, sample) = buf.split_at_mut(pn_offset + 4);
|
||||
let (first, rest) = header.split_at_mut(1);
|
||||
let sample = &sample[..hpk.sample_len()];
|
||||
hpk.encrypt_in_place(sample, &mut first[0], dbg!(rest))
|
||||
.unwrap();
|
||||
|
||||
const PROTECTED: &[u8] = &[
|
||||
0x4c, 0xfe, 0x41, 0x89, 0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6, 0x90, 0x80, 0x57,
|
||||
0x5d, 0x79, 0x99, 0xc2, 0x5a, 0x5b, 0xfb,
|
||||
];
|
||||
|
||||
assert_eq!(&buf, PROTECTED);
|
||||
|
||||
let (header, sample) = buf.split_at_mut(pn_offset + 4);
|
||||
let (first, rest) = header.split_at_mut(1);
|
||||
let sample = &sample[..hpk.sample_len()];
|
||||
hpk.decrypt_in_place(sample, &mut first[0], rest)
|
||||
.unwrap();
|
||||
|
||||
let (header, payload_tag) = buf.split_at_mut(4);
|
||||
let plain = packet
|
||||
.decrypt_in_place(PN, &*header, payload_tag)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plain, &PLAIN[4..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_update_test_vector() {
|
||||
fn equal_prk(x: &hkdf::Prk, y: &hkdf::Prk) -> bool {
|
||||
let mut x_data = [0; 16];
|
||||
let mut y_data = [0; 16];
|
||||
let x_okm = x
|
||||
.expand(&[b"info"], &aead::quic::AES_128)
|
||||
.unwrap();
|
||||
x_okm.fill(&mut x_data[..]).unwrap();
|
||||
let y_okm = y
|
||||
.expand(&[b"info"], &aead::quic::AES_128)
|
||||
.unwrap();
|
||||
y_okm.fill(&mut y_data[..]).unwrap();
|
||||
x_data == y_data
|
||||
}
|
||||
|
||||
let mut secrets = Secrets {
|
||||
// Constant dummy values for reproducibility
|
||||
client: hkdf::Prk::new_less_safe(
|
||||
hkdf::HKDF_SHA256,
|
||||
&[
|
||||
0xb8, 0x76, 0x77, 0x08, 0xf8, 0x77, 0x23, 0x58, 0xa6, 0xea, 0x9f, 0xc4, 0x3e,
|
||||
0x4a, 0xdd, 0x2c, 0x96, 0x1b, 0x3f, 0x52, 0x87, 0xa6, 0xd1, 0x46, 0x7e, 0xe0,
|
||||
0xae, 0xab, 0x33, 0x72, 0x4d, 0xbf,
|
||||
],
|
||||
),
|
||||
server: hkdf::Prk::new_less_safe(
|
||||
hkdf::HKDF_SHA256,
|
||||
&[
|
||||
0x42, 0xdc, 0x97, 0x21, 0x40, 0xe0, 0xf2, 0xe3, 0x98, 0x45, 0xb7, 0x67, 0x61,
|
||||
0x34, 0x39, 0xdc, 0x67, 0x58, 0xca, 0x43, 0x25, 0x9b, 0x87, 0x85, 0x06, 0x82,
|
||||
0x4e, 0xb1, 0xe4, 0x38, 0xd8, 0x55,
|
||||
],
|
||||
),
|
||||
suite: TLS13_AES_128_GCM_SHA256_INTERNAL,
|
||||
is_client: true,
|
||||
};
|
||||
secrets.update();
|
||||
|
||||
assert!(equal_prk(
|
||||
&secrets.client,
|
||||
&hkdf::Prk::new_less_safe(
|
||||
hkdf::HKDF_SHA256,
|
||||
&[
|
||||
0x42, 0xca, 0xc8, 0xc9, 0x1c, 0xd5, 0xeb, 0x40, 0x68, 0x2e, 0x43, 0x2e, 0xdf,
|
||||
0x2d, 0x2b, 0xe9, 0xf4, 0x1a, 0x52, 0xca, 0x6b, 0x22, 0xd8, 0xe6, 0xcd, 0xb1,
|
||||
0xe8, 0xac, 0xa9, 0x6, 0x1f, 0xce
|
||||
]
|
||||
)
|
||||
));
|
||||
assert!(equal_prk(
|
||||
&secrets.server,
|
||||
&hkdf::Prk::new_less_safe(
|
||||
hkdf::HKDF_SHA256,
|
||||
&[
|
||||
0xeb, 0x7f, 0x5e, 0x2a, 0x12, 0x3f, 0x40, 0x7d, 0xb4, 0x99, 0xe3, 0x61, 0xca,
|
||||
0xe5, 0x90, 0xd4, 0xd9, 0x92, 0xe1, 0x4b, 0x7a, 0xce, 0x3, 0xc2, 0x44, 0xe0,
|
||||
0x42, 0x21, 0x15, 0xb6, 0xd3, 0x8a
|
||||
]
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
30
third_party/rustls-fork-shadow-tls/src/rand.rs
vendored
Normal file
30
third_party/rustls-fork-shadow-tls/src/rand.rs
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
use crate::msgs::codec;
|
||||
/// The single place where we generate random material
|
||||
/// for our own use. These functions never fail,
|
||||
/// they panic on error.
|
||||
use ring::rand::{SecureRandom, SystemRandom};
|
||||
|
||||
/// Fill the whole slice with random material.
|
||||
pub(crate) fn fill_random(bytes: &mut [u8]) -> Result<(), GetRandomFailed> {
|
||||
SystemRandom::new()
|
||||
.fill(bytes)
|
||||
.map_err(|_| GetRandomFailed)
|
||||
}
|
||||
|
||||
/// Make a Vec<u8> of the given size
|
||||
/// containing random material.
|
||||
pub(crate) fn random_vec(len: usize) -> Result<Vec<u8>, GetRandomFailed> {
|
||||
let mut v = vec![0; len];
|
||||
fill_random(&mut v)?;
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// Return a uniformly random u32.
|
||||
pub(crate) fn random_u32() -> Result<u32, GetRandomFailed> {
|
||||
let mut buf = [0u8; 4];
|
||||
fill_random(&mut buf)?;
|
||||
codec::decode_u32(&buf).ok_or(GetRandomFailed)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GetRandomFailed;
|
||||
192
third_party/rustls-fork-shadow-tls/src/record_layer.rs
vendored
Normal file
192
third_party/rustls-fork-shadow-tls/src/record_layer.rs
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
use crate::cipher::{MessageDecrypter, MessageEncrypter};
|
||||
use crate::error::Error;
|
||||
use crate::msgs::message::{BorrowedPlainMessage, OpaqueMessage, PlainMessage};
|
||||
|
||||
static SEQ_SOFT_LIMIT: u64 = 0xffff_ffff_ffff_0000u64;
|
||||
static SEQ_HARD_LIMIT: u64 = 0xffff_ffff_ffff_fffeu64;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum DirectionState {
|
||||
/// No keying material.
|
||||
Invalid,
|
||||
|
||||
/// Keying material present, but not yet in use.
|
||||
Prepared,
|
||||
|
||||
/// Keying material in use.
|
||||
Active,
|
||||
}
|
||||
|
||||
pub(crate) struct RecordLayer {
|
||||
message_encrypter: Box<dyn MessageEncrypter>,
|
||||
message_decrypter: Box<dyn MessageDecrypter>,
|
||||
write_seq: u64,
|
||||
read_seq: u64,
|
||||
encrypt_state: DirectionState,
|
||||
decrypt_state: DirectionState,
|
||||
|
||||
// Message encrypted with other keys may be encountered, so failures
|
||||
// should be swallowed by the caller. This struct tracks the amount
|
||||
// of message size this is allowed for.
|
||||
trial_decryption_len: Option<usize>,
|
||||
}
|
||||
|
||||
impl RecordLayer {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
message_encrypter: <dyn MessageEncrypter>::invalid(),
|
||||
message_decrypter: <dyn MessageDecrypter>::invalid(),
|
||||
write_seq: 0,
|
||||
read_seq: 0,
|
||||
encrypt_state: DirectionState::Invalid,
|
||||
decrypt_state: DirectionState::Invalid,
|
||||
trial_decryption_len: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_encrypting(&self) -> bool {
|
||||
self.encrypt_state == DirectionState::Active
|
||||
}
|
||||
|
||||
pub(crate) fn is_decrypting(&self) -> bool {
|
||||
self.decrypt_state == DirectionState::Active
|
||||
}
|
||||
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub(crate) fn write_seq(&self) -> u64 {
|
||||
self.write_seq
|
||||
}
|
||||
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub(crate) fn read_seq(&self) -> u64 {
|
||||
self.read_seq
|
||||
}
|
||||
|
||||
pub(crate) fn doing_trial_decryption(&mut self, requested: usize) -> bool {
|
||||
match self
|
||||
.trial_decryption_len
|
||||
.and_then(|value| value.checked_sub(requested))
|
||||
{
|
||||
Some(remaining) => {
|
||||
self.trial_decryption_len = Some(remaining);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepare to use the given `MessageEncrypter` for future message encryption.
|
||||
/// It is not used until you call `start_encrypting`.
|
||||
pub(crate) fn prepare_message_encrypter(&mut self, cipher: Box<dyn MessageEncrypter>) {
|
||||
self.message_encrypter = cipher;
|
||||
self.write_seq = 0;
|
||||
self.encrypt_state = DirectionState::Prepared;
|
||||
}
|
||||
|
||||
/// Prepare to use the given `MessageDecrypter` for future message decryption.
|
||||
/// It is not used until you call `start_decrypting`.
|
||||
pub(crate) fn prepare_message_decrypter(&mut self, cipher: Box<dyn MessageDecrypter>) {
|
||||
self.message_decrypter = cipher;
|
||||
self.read_seq = 0;
|
||||
self.decrypt_state = DirectionState::Prepared;
|
||||
}
|
||||
|
||||
/// Start using the `MessageEncrypter` previously provided to the previous
|
||||
/// call to `prepare_message_encrypter`.
|
||||
pub(crate) fn start_encrypting(&mut self) {
|
||||
debug_assert!(self.encrypt_state == DirectionState::Prepared);
|
||||
self.encrypt_state = DirectionState::Active;
|
||||
}
|
||||
|
||||
/// Start using the `MessageDecrypter` previously provided to the previous
|
||||
/// call to `prepare_message_decrypter`.
|
||||
pub(crate) fn start_decrypting(&mut self) {
|
||||
debug_assert!(self.decrypt_state == DirectionState::Prepared);
|
||||
self.decrypt_state = DirectionState::Active;
|
||||
}
|
||||
|
||||
/// Set and start using the given `MessageEncrypter` for future outgoing
|
||||
/// message encryption.
|
||||
pub(crate) fn set_message_encrypter(&mut self, cipher: Box<dyn MessageEncrypter>) {
|
||||
self.prepare_message_encrypter(cipher);
|
||||
self.start_encrypting();
|
||||
}
|
||||
|
||||
/// Set and start using the given `MessageDecrypter` for future incoming
|
||||
/// message decryption.
|
||||
pub(crate) fn set_message_decrypter(&mut self, cipher: Box<dyn MessageDecrypter>) {
|
||||
self.prepare_message_decrypter(cipher);
|
||||
self.start_decrypting();
|
||||
self.trial_decryption_len = None;
|
||||
}
|
||||
|
||||
/// Set and start using the given `MessageDecrypter` for future incoming
|
||||
/// message decryption, and enable "trial decryption" mode for when TLS1.3
|
||||
/// 0-RTT is attempted but rejected by the server.
|
||||
pub(crate) fn set_message_decrypter_with_trial_decryption(
|
||||
&mut self,
|
||||
cipher: Box<dyn MessageDecrypter>,
|
||||
max_length: usize,
|
||||
) {
|
||||
self.prepare_message_decrypter(cipher);
|
||||
self.start_decrypting();
|
||||
self.trial_decryption_len = Some(max_length);
|
||||
}
|
||||
|
||||
pub(crate) fn finish_trial_decryption(&mut self) {
|
||||
self.trial_decryption_len = None;
|
||||
}
|
||||
|
||||
/// Return true if the peer appears to getting close to encrypting
|
||||
/// too many messages with this key.
|
||||
///
|
||||
/// Perhaps if we send an alert well before their counter wraps, a
|
||||
/// buggy peer won't make a terrible mistake here?
|
||||
///
|
||||
/// Note that there's no reason to refuse to decrypt: the security
|
||||
/// failure has already happened.
|
||||
pub(crate) fn wants_close_before_decrypt(&self) -> bool {
|
||||
self.read_seq == SEQ_SOFT_LIMIT
|
||||
}
|
||||
|
||||
/// Return true if we are getting close to encrypting too many
|
||||
/// messages with our encryption key.
|
||||
pub(crate) fn wants_close_before_encrypt(&self) -> bool {
|
||||
self.write_seq == SEQ_SOFT_LIMIT
|
||||
}
|
||||
|
||||
/// Return true if we outright refuse to do anything with the
|
||||
/// encryption key.
|
||||
pub(crate) fn encrypt_exhausted(&self) -> bool {
|
||||
self.write_seq >= SEQ_HARD_LIMIT
|
||||
}
|
||||
|
||||
/// Decrypt a TLS message.
|
||||
///
|
||||
/// `encr` is a decoded message allegedly received from the peer.
|
||||
/// If it can be decrypted, its decryption is returned. Otherwise,
|
||||
/// an error is returned.
|
||||
pub(crate) fn decrypt_incoming(&mut self, encr: OpaqueMessage) -> Result<PlainMessage, Error> {
|
||||
debug_assert!(self.is_decrypting());
|
||||
let seq = self.read_seq;
|
||||
let msg = self
|
||||
.message_decrypter
|
||||
.decrypt(encr, seq)?;
|
||||
self.read_seq += 1;
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Encrypt a TLS message.
|
||||
///
|
||||
/// `plain` is a TLS message we'd like to send. This function
|
||||
/// panics if the requisite keying material hasn't been established yet.
|
||||
pub(crate) fn encrypt_outgoing(&mut self, plain: BorrowedPlainMessage) -> OpaqueMessage {
|
||||
debug_assert!(self.encrypt_state == DirectionState::Active);
|
||||
assert!(!self.encrypt_exhausted());
|
||||
let seq = self.write_seq;
|
||||
self.write_seq += 1;
|
||||
self.message_encrypter
|
||||
.encrypt(plain, seq)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
116
third_party/rustls-fork-shadow-tls/src/server/builder.rs
vendored
Normal file
116
third_party/rustls-fork-shadow-tls/src/server/builder.rs
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
use crate::builder::{ConfigBuilder, WantsVerifier};
|
||||
use crate::error::Error;
|
||||
use crate::key;
|
||||
use crate::kx::SupportedKxGroup;
|
||||
use crate::server::handy;
|
||||
use crate::server::{ResolvesServerCert, ServerConfig};
|
||||
use crate::suites::SupportedCipherSuite;
|
||||
use crate::verify;
|
||||
use crate::versions;
|
||||
use crate::NoKeyLog;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
impl ConfigBuilder<ServerConfig, WantsVerifier> {
|
||||
/// Choose how to verify client certificates.
|
||||
pub fn with_client_cert_verifier(
|
||||
self,
|
||||
client_cert_verifier: Arc<dyn verify::ClientCertVerifier>,
|
||||
) -> ConfigBuilder<ServerConfig, WantsServerCert> {
|
||||
ConfigBuilder {
|
||||
state: WantsServerCert {
|
||||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: self.state.kx_groups,
|
||||
versions: self.state.versions,
|
||||
verifier: client_cert_verifier,
|
||||
},
|
||||
side: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable client authentication.
|
||||
pub fn with_no_client_auth(self) -> ConfigBuilder<ServerConfig, WantsServerCert> {
|
||||
self.with_client_cert_verifier(verify::NoClientAuth::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// A config builder state where the caller must supply how to provide a server certificate to
|
||||
/// the connecting peer.
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WantsServerCert {
|
||||
cipher_suites: Vec<SupportedCipherSuite>,
|
||||
kx_groups: Vec<&'static SupportedKxGroup>,
|
||||
versions: versions::EnabledVersions,
|
||||
verifier: Arc<dyn verify::ClientCertVerifier>,
|
||||
}
|
||||
|
||||
impl ConfigBuilder<ServerConfig, WantsServerCert> {
|
||||
/// Sets a single certificate chain and matching private key. This
|
||||
/// certificate and key is used for all subsequent connections,
|
||||
/// irrespective of things like SNI hostname.
|
||||
///
|
||||
/// Note that the end-entity certificate must have the
|
||||
/// [Subject Alternative Name](https://tools.ietf.org/html/rfc6125#section-4.1)
|
||||
/// extension to describe, e.g., the valid DNS name. The `commonName` field is
|
||||
/// disregarded.
|
||||
///
|
||||
/// `cert_chain` is a vector of DER-encoded certificates.
|
||||
/// `key_der` is a DER-encoded RSA, ECDSA, or Ed25519 private key.
|
||||
///
|
||||
/// This function fails if `key_der` is invalid.
|
||||
pub fn with_single_cert(
|
||||
self,
|
||||
cert_chain: Vec<key::Certificate>,
|
||||
key_der: key::PrivateKey,
|
||||
) -> Result<ServerConfig, Error> {
|
||||
let resolver = handy::AlwaysResolvesChain::new(cert_chain, &key_der)?;
|
||||
Ok(self.with_cert_resolver(Arc::new(resolver)))
|
||||
}
|
||||
|
||||
/// Sets a single certificate chain, matching private key, OCSP
|
||||
/// response and SCTs. This certificate and key is used for all
|
||||
/// subsequent connections, irrespective of things like SNI hostname.
|
||||
///
|
||||
/// `cert_chain` is a vector of DER-encoded certificates.
|
||||
/// `key_der` is a DER-encoded RSA, ECDSA, or Ed25519 private key.
|
||||
/// `ocsp` is a DER-encoded OCSP response. Ignored if zero length.
|
||||
/// `scts` is an `SignedCertificateTimestampList` encoding (see RFC6962)
|
||||
/// and is ignored if empty.
|
||||
///
|
||||
/// This function fails if `key_der` is invalid.
|
||||
pub fn with_single_cert_with_ocsp_and_sct(
|
||||
self,
|
||||
cert_chain: Vec<key::Certificate>,
|
||||
key_der: key::PrivateKey,
|
||||
ocsp: Vec<u8>,
|
||||
scts: Vec<u8>,
|
||||
) -> Result<ServerConfig, Error> {
|
||||
let resolver =
|
||||
handy::AlwaysResolvesChain::new_with_extras(cert_chain, &key_der, ocsp, scts)?;
|
||||
Ok(self.with_cert_resolver(Arc::new(resolver)))
|
||||
}
|
||||
|
||||
/// Sets a custom [`ResolvesServerCert`].
|
||||
pub fn with_cert_resolver(self, cert_resolver: Arc<dyn ResolvesServerCert>) -> ServerConfig {
|
||||
ServerConfig {
|
||||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: self.state.kx_groups,
|
||||
verifier: self.state.verifier,
|
||||
cert_resolver,
|
||||
ignore_client_order: false,
|
||||
max_fragment_size: None,
|
||||
session_storage: handy::ServerSessionMemoryCache::new(256),
|
||||
ticketer: Arc::new(handy::NeverProducesTickets {}),
|
||||
alpn_protocols: Vec::new(),
|
||||
versions: self.state.versions,
|
||||
key_log: Arc::new(NoKeyLog {}),
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
enable_secret_extraction: false,
|
||||
max_early_data_size: 0,
|
||||
send_half_rtt_data: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
41
third_party/rustls-fork-shadow-tls/src/server/common.rs
vendored
Normal file
41
third_party/rustls-fork-shadow-tls/src/server/common.rs
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
use crate::{key, sign};
|
||||
|
||||
/// ActiveCertifiedKey wraps CertifiedKey and tracks OSCP and SCT state
|
||||
/// in a single handshake.
|
||||
pub(super) struct ActiveCertifiedKey<'a> {
|
||||
key: &'a sign::CertifiedKey,
|
||||
ocsp: Option<&'a [u8]>,
|
||||
sct_list: Option<&'a [u8]>,
|
||||
}
|
||||
|
||||
impl<'a> ActiveCertifiedKey<'a> {
|
||||
pub(super) fn from_certified_key(key: &sign::CertifiedKey) -> ActiveCertifiedKey {
|
||||
ActiveCertifiedKey {
|
||||
key,
|
||||
ocsp: key.ocsp.as_deref(),
|
||||
sct_list: key.sct_list.as_deref(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the certificate chain
|
||||
#[inline]
|
||||
pub(super) fn get_cert(&self) -> &[key::Certificate] {
|
||||
&self.key.cert
|
||||
}
|
||||
|
||||
/// Get the signing key
|
||||
#[inline]
|
||||
pub(super) fn get_key(&self) -> &dyn sign::SigningKey {
|
||||
&*self.key.key
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn get_ocsp(&self) -> Option<&[u8]> {
|
||||
self.ocsp
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn get_sct_list(&self) -> Option<&[u8]> {
|
||||
self.sct_list
|
||||
}
|
||||
}
|
||||
278
third_party/rustls-fork-shadow-tls/src/server/handy.rs
vendored
Normal file
278
third_party/rustls-fork-shadow-tls/src/server/handy.rs
vendored
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
use crate::error::Error;
|
||||
use crate::key;
|
||||
use crate::limited_cache;
|
||||
use crate::server;
|
||||
use crate::server::ClientHello;
|
||||
use crate::sign;
|
||||
|
||||
use std::collections;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Something which never stores sessions.
|
||||
pub struct NoServerSessionStorage {}
|
||||
|
||||
impl server::StoresServerSessions for NoServerSessionStorage {
|
||||
fn put(&self, _id: Vec<u8>, _sec: Vec<u8>) -> bool {
|
||||
false
|
||||
}
|
||||
fn get(&self, _id: &[u8]) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
fn take(&self, _id: &[u8]) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
fn can_cache(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementer of `StoresServerSessions` that stores everything
|
||||
/// in memory. If enforces a limit on the number of stored sessions
|
||||
/// to bound memory usage.
|
||||
pub struct ServerSessionMemoryCache {
|
||||
cache: Mutex<limited_cache::LimitedCache<Vec<u8>, Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl ServerSessionMemoryCache {
|
||||
/// Make a new ServerSessionMemoryCache. `size` is the maximum
|
||||
/// number of stored sessions, and may be rounded-up for
|
||||
/// efficiency.
|
||||
pub fn new(size: usize) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
cache: Mutex::new(limited_cache::LimitedCache::new(size)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl server::StoresServerSessions for ServerSessionMemoryCache {
|
||||
fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool {
|
||||
self.cache
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(key, value);
|
||||
true
|
||||
}
|
||||
|
||||
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.cache
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(key)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn take(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.cache.lock().unwrap().remove(key)
|
||||
}
|
||||
|
||||
fn can_cache(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Something which never produces tickets.
|
||||
pub(super) struct NeverProducesTickets {}
|
||||
|
||||
impl server::ProducesTickets for NeverProducesTickets {
|
||||
fn enabled(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn lifetime(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
fn encrypt(&self, _bytes: &[u8]) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
fn decrypt(&self, _bytes: &[u8]) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Something which always resolves to the same cert chain.
|
||||
pub(super) struct AlwaysResolvesChain(Arc<sign::CertifiedKey>);
|
||||
|
||||
impl AlwaysResolvesChain {
|
||||
/// Creates an `AlwaysResolvesChain`, auto-detecting the underlying private
|
||||
/// key type and encoding.
|
||||
pub(super) fn new(
|
||||
chain: Vec<key::Certificate>,
|
||||
priv_key: &key::PrivateKey,
|
||||
) -> Result<Self, Error> {
|
||||
let key = sign::any_supported_type(priv_key)
|
||||
.map_err(|_| Error::General("invalid private key".into()))?;
|
||||
Ok(Self(Arc::new(sign::CertifiedKey::new(chain, key))))
|
||||
}
|
||||
|
||||
/// Creates an `AlwaysResolvesChain`, auto-detecting the underlying private
|
||||
/// key type and encoding.
|
||||
///
|
||||
/// If non-empty, the given OCSP response and SCTs are attached.
|
||||
pub(super) fn new_with_extras(
|
||||
chain: Vec<key::Certificate>,
|
||||
priv_key: &key::PrivateKey,
|
||||
ocsp: Vec<u8>,
|
||||
scts: Vec<u8>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut r = Self::new(chain, priv_key)?;
|
||||
|
||||
{
|
||||
let cert = Arc::make_mut(&mut r.0);
|
||||
if !ocsp.is_empty() {
|
||||
cert.ocsp = Some(ocsp);
|
||||
}
|
||||
if !scts.is_empty() {
|
||||
cert.sct_list = Some(scts);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl server::ResolvesServerCert for AlwaysResolvesChain {
|
||||
fn resolve(&self, _client_hello: ClientHello) -> Option<Arc<sign::CertifiedKey>> {
|
||||
Some(Arc::clone(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that resolves do different cert chains/keys based
|
||||
/// on client-supplied server name (via SNI).
|
||||
pub struct ResolvesServerCertUsingSni {
|
||||
by_name: collections::HashMap<String, Arc<sign::CertifiedKey>>,
|
||||
}
|
||||
|
||||
impl ResolvesServerCertUsingSni {
|
||||
/// Create a new and empty (i.e., knows no certificates) resolver.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
by_name: collections::HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a new `sign::CertifiedKey` to be used for the given SNI `name`.
|
||||
///
|
||||
/// This function fails if `name` is not a valid DNS name, or if
|
||||
/// it's not valid for the supplied certificate, or if the certificate
|
||||
/// chain is syntactically faulty.
|
||||
pub fn add(&mut self, name: &str, ck: sign::CertifiedKey) -> Result<(), Error> {
|
||||
let checked_name = webpki::DnsNameRef::try_from_ascii_str(name)
|
||||
.map_err(|_| Error::General("Bad DNS name".into()))?
|
||||
.to_owned();
|
||||
|
||||
ck.cross_check_end_entity_cert(Some(checked_name.as_ref()))?;
|
||||
let as_str: &str = checked_name.as_ref().into();
|
||||
self.by_name
|
||||
.insert(as_str.to_string(), Arc::new(ck));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl server::ResolvesServerCert for ResolvesServerCertUsingSni {
|
||||
fn resolve(&self, client_hello: ClientHello) -> Option<Arc<sign::CertifiedKey>> {
|
||||
if let Some(name) = client_hello.server_name() {
|
||||
self.by_name.get(name).map(Arc::clone)
|
||||
} else {
|
||||
// This kind of resolver requires SNI
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::server::ProducesTickets;
|
||||
use crate::server::ResolvesServerCert;
|
||||
use crate::server::StoresServerSessions;
|
||||
|
||||
#[test]
|
||||
fn test_noserversessionstorage_drops_put() {
|
||||
let c = NoServerSessionStorage {};
|
||||
assert!(!c.put(vec![0x01], vec![0x02]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_noserversessionstorage_denies_gets() {
|
||||
let c = NoServerSessionStorage {};
|
||||
c.put(vec![0x01], vec![0x02]);
|
||||
assert_eq!(c.get(&[]), None);
|
||||
assert_eq!(c.get(&[0x01]), None);
|
||||
assert_eq!(c.get(&[0x02]), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_noserversessionstorage_denies_takes() {
|
||||
let c = NoServerSessionStorage {};
|
||||
assert_eq!(c.take(&[]), None);
|
||||
assert_eq!(c.take(&[0x01]), None);
|
||||
assert_eq!(c.take(&[0x02]), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serversessionmemorycache_accepts_put() {
|
||||
let c = ServerSessionMemoryCache::new(4);
|
||||
assert!(c.put(vec![0x01], vec![0x02]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serversessionmemorycache_persists_put() {
|
||||
let c = ServerSessionMemoryCache::new(4);
|
||||
assert!(c.put(vec![0x01], vec![0x02]));
|
||||
assert_eq!(c.get(&[0x01]), Some(vec![0x02]));
|
||||
assert_eq!(c.get(&[0x01]), Some(vec![0x02]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serversessionmemorycache_overwrites_put() {
|
||||
let c = ServerSessionMemoryCache::new(4);
|
||||
assert!(c.put(vec![0x01], vec![0x02]));
|
||||
assert!(c.put(vec![0x01], vec![0x04]));
|
||||
assert_eq!(c.get(&[0x01]), Some(vec![0x04]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serversessionmemorycache_drops_to_maintain_size_invariant() {
|
||||
let c = ServerSessionMemoryCache::new(2);
|
||||
assert!(c.put(vec![0x01], vec![0x02]));
|
||||
assert!(c.put(vec![0x03], vec![0x04]));
|
||||
assert!(c.put(vec![0x05], vec![0x06]));
|
||||
assert!(c.put(vec![0x07], vec![0x08]));
|
||||
assert!(c.put(vec![0x09], vec![0x0a]));
|
||||
|
||||
let count = c.get(&[0x01]).iter().count()
|
||||
+ c.get(&[0x03]).iter().count()
|
||||
+ c.get(&[0x05]).iter().count()
|
||||
+ c.get(&[0x07]).iter().count()
|
||||
+ c.get(&[0x09]).iter().count();
|
||||
|
||||
assert!(count < 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_neverproducestickets_does_nothing() {
|
||||
let npt = NeverProducesTickets {};
|
||||
assert!(!npt.enabled());
|
||||
assert_eq!(0, npt.lifetime());
|
||||
assert_eq!(None, npt.encrypt(&[]));
|
||||
assert_eq!(None, npt.decrypt(&[]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolvesservercertusingsni_requires_sni() {
|
||||
let rscsni = ResolvesServerCertUsingSni::new();
|
||||
assert!(rscsni
|
||||
.resolve(ClientHello::new(&None, &[], None, &[]))
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolvesservercertusingsni_handles_unknown_name() {
|
||||
let rscsni = ResolvesServerCertUsingSni::new();
|
||||
let name = webpki::DnsNameRef::try_from_ascii_str("hello.com")
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
assert!(rscsni
|
||||
.resolve(ClientHello::new(&Some(name), &[], None, &[]))
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
522
third_party/rustls-fork-shadow-tls/src/server/hs.rs
vendored
Normal file
522
third_party/rustls-fork-shadow-tls/src/server/hs.rs
vendored
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
use crate::conn::{CommonState, ConnectionRandoms, State};
|
||||
#[cfg(feature = "tls12")]
|
||||
use crate::enums::CipherSuite;
|
||||
use crate::enums::{ProtocolVersion, SignatureScheme};
|
||||
use crate::error::Error;
|
||||
use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer};
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::{debug, trace};
|
||||
use crate::msgs::enums::HandshakeType;
|
||||
use crate::msgs::enums::{AlertDescription, Compression, ExtensionType};
|
||||
#[cfg(feature = "tls12")]
|
||||
use crate::msgs::handshake::SessionID;
|
||||
use crate::msgs::handshake::{ClientHelloPayload, Random, ServerExtension};
|
||||
use crate::msgs::handshake::{ConvertProtocolNameList, ConvertServerNameList, HandshakePayload};
|
||||
use crate::msgs::message::{Message, MessagePayload};
|
||||
use crate::msgs::persist;
|
||||
use crate::server::{ClientHello, ServerConfig};
|
||||
use crate::suites;
|
||||
use crate::SupportedCipherSuite;
|
||||
|
||||
use super::server_conn::ServerConnectionData;
|
||||
#[cfg(feature = "tls12")]
|
||||
use super::tls12;
|
||||
use crate::server::common::ActiveCertifiedKey;
|
||||
use crate::server::tls13;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(super) type NextState = Box<dyn State<ServerConnectionData>>;
|
||||
pub(super) type NextStateOrError = Result<NextState, Error>;
|
||||
pub(super) type ServerContext<'a> = crate::conn::Context<'a, ServerConnectionData>;
|
||||
|
||||
pub(super) fn incompatible(common: &mut CommonState, why: &str) -> Error {
|
||||
common.send_fatal_alert(AlertDescription::HandshakeFailure);
|
||||
Error::PeerIncompatibleError(why.to_string())
|
||||
}
|
||||
|
||||
fn bad_version(common: &mut CommonState, why: &str) -> Error {
|
||||
common.send_fatal_alert(AlertDescription::ProtocolVersion);
|
||||
Error::PeerIncompatibleError(why.to_string())
|
||||
}
|
||||
|
||||
pub(super) fn decode_error(common: &mut CommonState, why: &str) -> Error {
|
||||
common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
Error::PeerMisbehavedError(why.to_string())
|
||||
}
|
||||
|
||||
pub(super) fn can_resume(
|
||||
suite: SupportedCipherSuite,
|
||||
sni: &Option<webpki::DnsName>,
|
||||
using_ems: bool,
|
||||
resumedata: &persist::ServerSessionValue,
|
||||
) -> bool {
|
||||
// The RFCs underspecify what happens if we try to resume to
|
||||
// an unoffered/varying suite. We merely don't resume in weird cases.
|
||||
//
|
||||
// RFC 6066 says "A server that implements this extension MUST NOT accept
|
||||
// the request to resume the session if the server_name extension contains
|
||||
// a different name. Instead, it proceeds with a full handshake to
|
||||
// establish a new session."
|
||||
resumedata.cipher_suite == suite.suite()
|
||||
&& (resumedata.extended_ms == using_ems || (resumedata.extended_ms && !using_ems))
|
||||
&& &resumedata.sni == sni
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct ExtensionProcessing {
|
||||
// extensions to reply with
|
||||
pub(super) exts: Vec<ServerExtension>,
|
||||
#[cfg(feature = "tls12")]
|
||||
pub(super) send_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExtensionProcessing {
|
||||
pub(super) fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub(super) fn process_common(
|
||||
&mut self,
|
||||
config: &ServerConfig,
|
||||
cx: &mut ServerContext<'_>,
|
||||
ocsp_response: &mut Option<&[u8]>,
|
||||
sct_list: &mut Option<&[u8]>,
|
||||
hello: &ClientHelloPayload,
|
||||
resumedata: Option<&persist::ServerSessionValue>,
|
||||
extra_exts: Vec<ServerExtension>,
|
||||
) -> Result<(), Error> {
|
||||
// ALPN
|
||||
let our_protocols = &config.alpn_protocols;
|
||||
let maybe_their_protocols = hello.get_alpn_extension();
|
||||
if let Some(their_protocols) = maybe_their_protocols {
|
||||
let their_protocols = their_protocols.to_slices();
|
||||
|
||||
if their_protocols
|
||||
.iter()
|
||||
.any(|protocol| protocol.is_empty())
|
||||
{
|
||||
return Err(Error::PeerMisbehavedError(
|
||||
"client offered empty ALPN protocol".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
cx.common.alpn_protocol = our_protocols
|
||||
.iter()
|
||||
.find(|protocol| their_protocols.contains(&protocol.as_slice()))
|
||||
.cloned();
|
||||
if let Some(ref selected_protocol) = cx.common.alpn_protocol {
|
||||
debug!("Chosen ALPN protocol {:?}", selected_protocol);
|
||||
self.exts
|
||||
.push(ServerExtension::make_alpn(&[selected_protocol]));
|
||||
} else if !our_protocols.is_empty() {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::NoApplicationProtocol);
|
||||
return Err(Error::NoApplicationProtocol);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
{
|
||||
if cx.common.is_quic() {
|
||||
// QUIC has strict ALPN, unlike TLS's more backwards-compatible behavior. RFC 9001
|
||||
// says: "The server MUST treat the inability to select a compatible application
|
||||
// protocol as a connection error of type 0x0178". We judge that ALPN was desired
|
||||
// (rather than some out-of-band protocol negotiation mechanism) iff any ALPN
|
||||
// protocols were configured locally or offered by the client. This helps prevent
|
||||
// successful establishment of connections between peers that can't understand
|
||||
// each other.
|
||||
if cx.common.alpn_protocol.is_none()
|
||||
&& (!our_protocols.is_empty() || maybe_their_protocols.is_some())
|
||||
{
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::NoApplicationProtocol);
|
||||
return Err(Error::NoApplicationProtocol);
|
||||
}
|
||||
|
||||
match hello.get_quic_params_extension() {
|
||||
Some(params) => cx.common.quic.params = Some(params),
|
||||
None => {
|
||||
return Err(cx
|
||||
.common
|
||||
.missing_extension("QUIC transport parameters not found"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let for_resume = resumedata.is_some();
|
||||
// SNI
|
||||
if !for_resume && hello.get_sni_extension().is_some() {
|
||||
self.exts
|
||||
.push(ServerExtension::ServerNameAck);
|
||||
}
|
||||
|
||||
// Send status_request response if we have one. This is not allowed
|
||||
// if we're resuming, and is only triggered if we have an OCSP response
|
||||
// to send.
|
||||
if !for_resume
|
||||
&& hello
|
||||
.find_extension(ExtensionType::StatusRequest)
|
||||
.is_some()
|
||||
{
|
||||
if ocsp_response.is_some() && !cx.common.is_tls13() {
|
||||
// Only TLS1.2 sends confirmation in ServerHello
|
||||
self.exts
|
||||
.push(ServerExtension::CertificateStatusAck);
|
||||
}
|
||||
} else {
|
||||
// Throw away any OCSP response so we don't try to send it later.
|
||||
ocsp_response.take();
|
||||
}
|
||||
|
||||
if !for_resume
|
||||
&& hello
|
||||
.find_extension(ExtensionType::SCT)
|
||||
.is_some()
|
||||
{
|
||||
if !cx.common.is_tls13() {
|
||||
// Take the SCT list, if any, so we don't send it later,
|
||||
// and put it in the legacy extension.
|
||||
if let Some(sct_list) = sct_list.take() {
|
||||
self.exts
|
||||
.push(ServerExtension::make_sct(sct_list.to_vec()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Throw away any SCT list so we don't send it later.
|
||||
sct_list.take();
|
||||
}
|
||||
|
||||
self.exts.extend(extra_exts);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
pub(super) fn process_tls12(
|
||||
&mut self,
|
||||
config: &ServerConfig,
|
||||
hello: &ClientHelloPayload,
|
||||
using_ems: bool,
|
||||
) {
|
||||
// Renegotiation.
|
||||
// (We don't do reneg at all, but would support the secure version if we did.)
|
||||
let secure_reneg_offered = hello
|
||||
.find_extension(ExtensionType::RenegotiationInfo)
|
||||
.is_some()
|
||||
|| hello
|
||||
.cipher_suites
|
||||
.contains(&CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
|
||||
|
||||
if secure_reneg_offered {
|
||||
self.exts
|
||||
.push(ServerExtension::make_empty_renegotiation_info());
|
||||
}
|
||||
|
||||
// Tickets:
|
||||
// If we get any SessionTicket extension and have tickets enabled,
|
||||
// we send an ack.
|
||||
if hello
|
||||
.find_extension(ExtensionType::SessionTicket)
|
||||
.is_some()
|
||||
&& config.ticketer.enabled()
|
||||
{
|
||||
self.send_ticket = true;
|
||||
self.exts
|
||||
.push(ServerExtension::SessionTicketAck);
|
||||
}
|
||||
|
||||
// Confirm use of EMS if offered.
|
||||
if using_ems {
|
||||
self.exts
|
||||
.push(ServerExtension::ExtendedMasterSecretAck);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct ExpectClientHello {
|
||||
pub(super) config: Arc<ServerConfig>,
|
||||
pub(super) extra_exts: Vec<ServerExtension>,
|
||||
pub(super) transcript: HandshakeHashOrBuffer,
|
||||
#[cfg(feature = "tls12")]
|
||||
pub(super) session_id: SessionID,
|
||||
#[cfg(feature = "tls12")]
|
||||
pub(super) using_ems: bool,
|
||||
pub(super) done_retry: bool,
|
||||
pub(super) send_ticket: bool,
|
||||
}
|
||||
|
||||
impl ExpectClientHello {
|
||||
pub(super) fn new(config: Arc<ServerConfig>, extra_exts: Vec<ServerExtension>) -> Self {
|
||||
let mut transcript_buffer = HandshakeHashBuffer::new();
|
||||
|
||||
if config.verifier.offer_client_auth() {
|
||||
transcript_buffer.set_client_auth_enabled();
|
||||
}
|
||||
|
||||
Self {
|
||||
config,
|
||||
extra_exts,
|
||||
transcript: HandshakeHashOrBuffer::Buffer(transcript_buffer),
|
||||
#[cfg(feature = "tls12")]
|
||||
session_id: SessionID::empty(),
|
||||
#[cfg(feature = "tls12")]
|
||||
using_ems: false,
|
||||
done_retry: false,
|
||||
send_ticket: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Continues handling of a `ClientHello` message once config and certificate are available.
|
||||
pub(super) fn with_certified_key(
|
||||
self,
|
||||
mut sig_schemes: Vec<SignatureScheme>,
|
||||
client_hello: &ClientHelloPayload,
|
||||
m: &Message,
|
||||
cx: &mut ServerContext<'_>,
|
||||
) -> NextStateOrError {
|
||||
let tls13_enabled = self
|
||||
.config
|
||||
.supports_version(ProtocolVersion::TLSv1_3);
|
||||
let tls12_enabled = self
|
||||
.config
|
||||
.supports_version(ProtocolVersion::TLSv1_2);
|
||||
|
||||
// Are we doing TLS1.3?
|
||||
let maybe_versions_ext = client_hello.get_versions_extension();
|
||||
let version = if let Some(versions) = maybe_versions_ext {
|
||||
if versions.contains(&ProtocolVersion::TLSv1_3) && tls13_enabled {
|
||||
ProtocolVersion::TLSv1_3
|
||||
} else if !versions.contains(&ProtocolVersion::TLSv1_2) || !tls12_enabled {
|
||||
return Err(bad_version(cx.common, "TLS1.2 not offered/enabled"));
|
||||
} else if cx.common.is_quic() {
|
||||
return Err(bad_version(
|
||||
cx.common,
|
||||
"Expecting QUIC connection, but client does not support TLSv1_3",
|
||||
));
|
||||
} else {
|
||||
ProtocolVersion::TLSv1_2
|
||||
}
|
||||
} else if client_hello.client_version.get_u16() < ProtocolVersion::TLSv1_2.get_u16() {
|
||||
return Err(bad_version(cx.common, "Client does not support TLSv1_2"));
|
||||
} else if !tls12_enabled && tls13_enabled {
|
||||
return Err(bad_version(
|
||||
cx.common,
|
||||
"Server requires TLS1.3, but client omitted versions ext",
|
||||
));
|
||||
} else if cx.common.is_quic() {
|
||||
return Err(bad_version(
|
||||
cx.common,
|
||||
"Expecting QUIC connection, but client does not support TLSv1_3",
|
||||
));
|
||||
} else {
|
||||
ProtocolVersion::TLSv1_2
|
||||
};
|
||||
|
||||
cx.common.negotiated_version = Some(version);
|
||||
|
||||
// We communicate to the upper layer what kind of key they should choose
|
||||
// via the sigschemes value. Clients tend to treat this extension
|
||||
// orthogonally to offered ciphersuites (even though, in TLS1.2 it is not).
|
||||
// So: reduce the offered sigschemes to those compatible with the
|
||||
// intersection of ciphersuites.
|
||||
let client_suites = self
|
||||
.config
|
||||
.cipher_suites
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|scs| {
|
||||
client_hello
|
||||
.cipher_suites
|
||||
.contains(&scs.suite())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
sig_schemes
|
||||
.retain(|scheme| suites::compatible_sigscheme_for_suites(*scheme, &client_suites));
|
||||
|
||||
// Choose a certificate.
|
||||
let certkey = {
|
||||
let client_hello = ClientHello::new(
|
||||
&cx.data.sni,
|
||||
&sig_schemes,
|
||||
client_hello.get_alpn_extension(),
|
||||
&client_hello.cipher_suites,
|
||||
);
|
||||
|
||||
let certkey = self
|
||||
.config
|
||||
.cert_resolver
|
||||
.resolve(client_hello);
|
||||
|
||||
certkey.ok_or_else(|| {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::AccessDenied);
|
||||
Error::General("no server certificate chain resolved".to_string())
|
||||
})?
|
||||
};
|
||||
let certkey = ActiveCertifiedKey::from_certified_key(&certkey);
|
||||
|
||||
// Reduce our supported ciphersuites by the certificate.
|
||||
// (no-op for TLS1.3)
|
||||
let suitable_suites =
|
||||
suites::reduce_given_sigalg(&self.config.cipher_suites, certkey.get_key().algorithm());
|
||||
|
||||
// And version
|
||||
let suitable_suites = suites::reduce_given_version(&suitable_suites, version);
|
||||
|
||||
let suite = if self.config.ignore_client_order {
|
||||
suites::choose_ciphersuite_preferring_server(
|
||||
&client_hello.cipher_suites,
|
||||
&suitable_suites,
|
||||
)
|
||||
} else {
|
||||
suites::choose_ciphersuite_preferring_client(
|
||||
&client_hello.cipher_suites,
|
||||
&suitable_suites,
|
||||
)
|
||||
}
|
||||
.ok_or_else(|| incompatible(cx.common, "no ciphersuites in common"))?;
|
||||
|
||||
debug!("decided upon suite {:?}", suite);
|
||||
cx.common.suite = Some(suite);
|
||||
|
||||
// Start handshake hash.
|
||||
let starting_hash = suite.hash_algorithm();
|
||||
let transcript = match self.transcript {
|
||||
HandshakeHashOrBuffer::Buffer(inner) => inner.start_hash(starting_hash),
|
||||
HandshakeHashOrBuffer::Hash(inner) if inner.algorithm() == starting_hash => inner,
|
||||
_ => {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("hash differed on retry"));
|
||||
}
|
||||
};
|
||||
|
||||
// Save their Random.
|
||||
let randoms = ConnectionRandoms::new(client_hello.random, Random::new()?);
|
||||
match suite {
|
||||
SupportedCipherSuite::Tls13(suite) => tls13::CompleteClientHelloHandling {
|
||||
config: self.config,
|
||||
transcript,
|
||||
suite,
|
||||
randoms,
|
||||
done_retry: self.done_retry,
|
||||
send_ticket: self.send_ticket,
|
||||
extra_exts: self.extra_exts,
|
||||
}
|
||||
.handle_client_hello(cx, certkey, m, client_hello, sig_schemes),
|
||||
#[cfg(feature = "tls12")]
|
||||
SupportedCipherSuite::Tls12(suite) => tls12::CompleteClientHelloHandling {
|
||||
config: self.config,
|
||||
transcript,
|
||||
session_id: self.session_id,
|
||||
suite,
|
||||
using_ems: self.using_ems,
|
||||
randoms,
|
||||
send_ticket: self.send_ticket,
|
||||
extra_exts: self.extra_exts,
|
||||
}
|
||||
.handle_client_hello(
|
||||
cx,
|
||||
certkey,
|
||||
m,
|
||||
client_hello,
|
||||
sig_schemes,
|
||||
tls13_enabled,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State<ServerConnectionData> for ExpectClientHello {
|
||||
fn handle(self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> NextStateOrError {
|
||||
let (client_hello, sig_schemes) =
|
||||
process_client_hello(&m, self.done_retry, cx.common, cx.data)?;
|
||||
self.with_certified_key(sig_schemes, client_hello, &m, cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration-independent validation of a `ClientHello` message.
|
||||
///
|
||||
/// This represents the first part of the `ClientHello` handling, where we do all validation that
|
||||
/// doesn't depend on a `ServerConfig` being available and extract everything needed to build a
|
||||
/// [`ClientHello`] value for a [`ResolvesServerConfig`]/`ResolvesServerCert`].
|
||||
///
|
||||
/// Note that this will modify `data.sni` even if config or certificate resolution fail.
|
||||
pub(super) fn process_client_hello<'a>(
|
||||
m: &'a Message,
|
||||
done_retry: bool,
|
||||
common: &mut CommonState,
|
||||
data: &mut ServerConnectionData,
|
||||
) -> Result<(&'a ClientHelloPayload, Vec<SignatureScheme>), Error> {
|
||||
let client_hello =
|
||||
require_handshake_msg!(m, HandshakeType::ClientHello, HandshakePayload::ClientHello)?;
|
||||
trace!("we got a clienthello {:?}", client_hello);
|
||||
|
||||
if !client_hello
|
||||
.compression_methods
|
||||
.contains(&Compression::Null)
|
||||
{
|
||||
common.send_fatal_alert(AlertDescription::IllegalParameter);
|
||||
return Err(Error::PeerIncompatibleError(
|
||||
"client did not offer Null compression".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if client_hello.has_duplicate_extension() {
|
||||
return Err(decode_error(common, "client sent duplicate extensions"));
|
||||
}
|
||||
|
||||
// No handshake messages should follow this one in this flight.
|
||||
common.check_aligned_handshake()?;
|
||||
|
||||
// Extract and validate the SNI DNS name, if any, before giving it to
|
||||
// the cert resolver. In particular, if it is invalid then we should
|
||||
// send an Illegal Parameter alert instead of the Internal Error alert
|
||||
// (or whatever) that we'd send if this were checked later or in a
|
||||
// different way.
|
||||
let sni: Option<webpki::DnsName> = match client_hello.get_sni_extension() {
|
||||
Some(sni) => {
|
||||
if sni.has_duplicate_names_for_type() {
|
||||
return Err(decode_error(
|
||||
common,
|
||||
"ClientHello SNI contains duplicate name types",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(hostname) = sni.get_single_hostname() {
|
||||
Some(hostname.into())
|
||||
} else {
|
||||
return Err(common.illegal_param("ClientHello SNI did not contain a hostname"));
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
// save only the first SNI
|
||||
if let (Some(sni), false) = (&sni, done_retry) {
|
||||
// Save the SNI into the session.
|
||||
// The SNI hostname is immutable once set.
|
||||
assert!(data.sni.is_none());
|
||||
data.sni = Some(sni.clone())
|
||||
} else if data.sni != sni {
|
||||
return Err(Error::PeerIncompatibleError(
|
||||
"SNI differed on retry".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let sig_schemes = client_hello
|
||||
.get_sigalgs_extension()
|
||||
.ok_or_else(|| incompatible(common, "client didn't describe signature schemes"))?
|
||||
.clone();
|
||||
|
||||
Ok((client_hello, sig_schemes))
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub(crate) enum HandshakeHashOrBuffer {
|
||||
Buffer(HandshakeHashBuffer),
|
||||
Hash(HandshakeHash),
|
||||
}
|
||||
849
third_party/rustls-fork-shadow-tls/src/server/server_conn.rs
vendored
Normal file
849
third_party/rustls-fork-shadow-tls/src/server/server_conn.rs
vendored
Normal file
|
|
@ -0,0 +1,849 @@
|
|||
use crate::builder::{ConfigBuilder, WantsCipherSuites};
|
||||
use crate::conn::{CommonState, ConnectionCommon, Side, State};
|
||||
use crate::enums::{CipherSuite, ProtocolVersion, SignatureScheme};
|
||||
use crate::error::Error;
|
||||
use crate::kx::SupportedKxGroup;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::trace;
|
||||
use crate::msgs::base::{Payload, PayloadU8};
|
||||
#[cfg(feature = "quic")]
|
||||
use crate::msgs::enums::AlertDescription;
|
||||
use crate::msgs::handshake::{ClientHelloPayload, ServerExtension};
|
||||
use crate::msgs::message::Message;
|
||||
use crate::sign;
|
||||
use crate::suites::SupportedCipherSuite;
|
||||
use crate::vecbuf::ChunkVecBuffer;
|
||||
use crate::verify;
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
use crate::ExtractedSecrets;
|
||||
use crate::KeyLog;
|
||||
#[cfg(feature = "quic")]
|
||||
use crate::{conn::Protocol, quic};
|
||||
|
||||
use super::hs;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// A trait for the ability to store server session data.
|
||||
///
|
||||
/// The keys and values are opaque.
|
||||
///
|
||||
/// Both the keys and values should be treated as
|
||||
/// **highly sensitive data**, containing enough key material
|
||||
/// to break all security of the corresponding sessions.
|
||||
///
|
||||
/// Implementations can be lossy (in other words, forgetting
|
||||
/// key/value pairs) without any negative security consequences.
|
||||
///
|
||||
/// However, note that `take` **must** reliably delete a returned
|
||||
/// value. If it does not, there may be security consequences.
|
||||
///
|
||||
/// `put` and `take` are mutating operations; this isn't expressed
|
||||
/// in the type system to allow implementations freedom in
|
||||
/// how to achieve interior mutability. `Mutex` is a common
|
||||
/// choice.
|
||||
pub trait StoresServerSessions: Send + Sync {
|
||||
/// Store session secrets encoded in `value` against `key`,
|
||||
/// overwrites any existing value against `key`. Returns `true`
|
||||
/// if the value was stored.
|
||||
fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool;
|
||||
|
||||
/// Find a value with the given `key`. Return it, or None
|
||||
/// if it doesn't exist.
|
||||
fn get(&self, key: &[u8]) -> Option<Vec<u8>>;
|
||||
|
||||
/// Find a value with the given `key`. Return it and delete it;
|
||||
/// or None if it doesn't exist.
|
||||
fn take(&self, key: &[u8]) -> Option<Vec<u8>>;
|
||||
|
||||
/// Whether the store can cache another session. This is used to indicate to clients
|
||||
/// whether their session can be resumed; the implementation is not required to remember
|
||||
/// a session even if it returns `true` here.
|
||||
fn can_cache(&self) -> bool;
|
||||
}
|
||||
|
||||
/// A trait for the ability to encrypt and decrypt tickets.
|
||||
pub trait ProducesTickets: Send + Sync {
|
||||
/// Returns true if this implementation will encrypt/decrypt
|
||||
/// tickets. Should return false if this is a dummy
|
||||
/// implementation: the server will not send the SessionTicket
|
||||
/// extension and will not call the other functions.
|
||||
fn enabled(&self) -> bool;
|
||||
|
||||
/// Returns the lifetime in seconds of tickets produced now.
|
||||
/// The lifetime is provided as a hint to clients that the
|
||||
/// ticket will not be useful after the given time.
|
||||
///
|
||||
/// This lifetime must be implemented by key rolling and
|
||||
/// erasure, *not* by storing a lifetime in the ticket.
|
||||
///
|
||||
/// The objective is to limit damage to forward secrecy caused
|
||||
/// by tickets, not just limiting their lifetime.
|
||||
fn lifetime(&self) -> u32;
|
||||
|
||||
/// Encrypt and authenticate `plain`, returning the resulting
|
||||
/// ticket. Return None if `plain` cannot be encrypted for
|
||||
/// some reason: an empty ticket will be sent and the connection
|
||||
/// will continue.
|
||||
fn encrypt(&self, plain: &[u8]) -> Option<Vec<u8>>;
|
||||
|
||||
/// Decrypt `cipher`, validating its authenticity protection
|
||||
/// and recovering the plaintext. `cipher` is fully attacker
|
||||
/// controlled, so this decryption must be side-channel free,
|
||||
/// panic-proof, and otherwise bullet-proof. If the decryption
|
||||
/// fails, return None.
|
||||
fn decrypt(&self, cipher: &[u8]) -> Option<Vec<u8>>;
|
||||
}
|
||||
|
||||
/// How to choose a certificate chain and signing key for use
|
||||
/// in server authentication.
|
||||
pub trait ResolvesServerCert: Send + Sync {
|
||||
/// Choose a certificate chain and matching key given simplified
|
||||
/// ClientHello information.
|
||||
///
|
||||
/// Return `None` to abort the handshake.
|
||||
fn resolve(&self, client_hello: ClientHello) -> Option<Arc<sign::CertifiedKey>>;
|
||||
}
|
||||
|
||||
/// A struct representing the received Client Hello
|
||||
pub struct ClientHello<'a> {
|
||||
server_name: &'a Option<webpki::DnsName>,
|
||||
signature_schemes: &'a [SignatureScheme],
|
||||
alpn: Option<&'a Vec<PayloadU8>>,
|
||||
cipher_suites: &'a [CipherSuite],
|
||||
}
|
||||
|
||||
impl<'a> ClientHello<'a> {
|
||||
/// Creates a new ClientHello
|
||||
pub(super) fn new(
|
||||
server_name: &'a Option<webpki::DnsName>,
|
||||
signature_schemes: &'a [SignatureScheme],
|
||||
alpn: Option<&'a Vec<PayloadU8>>,
|
||||
cipher_suites: &'a [CipherSuite],
|
||||
) -> Self {
|
||||
trace!("sni {:?}", server_name);
|
||||
trace!("sig schemes {:?}", signature_schemes);
|
||||
trace!("alpn protocols {:?}", alpn);
|
||||
trace!("cipher suites {:?}", cipher_suites);
|
||||
|
||||
ClientHello {
|
||||
server_name,
|
||||
signature_schemes,
|
||||
alpn,
|
||||
cipher_suites,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the server name indicator.
|
||||
///
|
||||
/// Returns `None` if the client did not supply a SNI.
|
||||
pub fn server_name(&self) -> Option<&str> {
|
||||
self.server_name
|
||||
.as_ref()
|
||||
.map(<webpki::DnsName as AsRef<str>>::as_ref)
|
||||
}
|
||||
|
||||
/// Get the compatible signature schemes.
|
||||
///
|
||||
/// Returns standard-specified default if the client omitted this extension.
|
||||
pub fn signature_schemes(&self) -> &[SignatureScheme] {
|
||||
self.signature_schemes
|
||||
}
|
||||
|
||||
/// Get the ALPN protocol identifiers submitted by the client.
|
||||
///
|
||||
/// Returns `None` if the client did not include an ALPN extension.
|
||||
///
|
||||
/// Application Layer Protocol Negotiation (ALPN) is a TLS extension that lets a client
|
||||
/// submit a set of identifiers that each a represent an application-layer protocol.
|
||||
/// The server will then pick its preferred protocol from the set submitted by the client.
|
||||
/// Each identifier is represented as a byte array, although common values are often ASCII-encoded.
|
||||
/// See the official RFC-7301 specifications at <https://datatracker.ietf.org/doc/html/rfc7301>
|
||||
/// for more information on ALPN.
|
||||
///
|
||||
/// For example, a HTTP client might specify "http/1.1" and/or "h2". Other well-known values
|
||||
/// are listed in the at IANA registry at
|
||||
/// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>.
|
||||
///
|
||||
/// The server can specify supported ALPN protocols by setting [`ServerConfig::alpn_protocols`].
|
||||
/// During the handshake, the server will select the first protocol configured that the client supports.
|
||||
pub fn alpn(&self) -> Option<impl Iterator<Item = &'a [u8]>> {
|
||||
self.alpn.map(|protocols| {
|
||||
protocols
|
||||
.iter()
|
||||
.map(|proto| proto.0.as_slice())
|
||||
})
|
||||
}
|
||||
|
||||
/// Get cipher suites.
|
||||
pub fn cipher_suites(&self) -> &[CipherSuite] {
|
||||
self.cipher_suites
|
||||
}
|
||||
}
|
||||
|
||||
/// Common configuration for a set of server sessions.
|
||||
///
|
||||
/// Making one of these can be expensive, and should be
|
||||
/// once per process rather than once per connection.
|
||||
///
|
||||
/// These must be created via the [`ServerConfig::builder()`] function.
|
||||
///
|
||||
/// # Defaults
|
||||
///
|
||||
/// * [`ServerConfig::max_fragment_size`]: the default is `None`: TLS packets are not fragmented to a specific size.
|
||||
/// * [`ServerConfig::session_storage`]: the default stores 256 sessions in memory.
|
||||
/// * [`ServerConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated.
|
||||
/// * [`ServerConfig::key_log`]: key material is not logged.
|
||||
#[derive(Clone)]
|
||||
pub struct ServerConfig {
|
||||
/// List of ciphersuites, in preference order.
|
||||
pub(super) cipher_suites: Vec<SupportedCipherSuite>,
|
||||
|
||||
/// List of supported key exchange groups.
|
||||
///
|
||||
/// The first is the highest priority: they will be
|
||||
/// offered to the client in this order.
|
||||
pub(super) kx_groups: Vec<&'static SupportedKxGroup>,
|
||||
|
||||
/// Ignore the client's ciphersuite order. Instead,
|
||||
/// choose the top ciphersuite in the server list
|
||||
/// which is supported by the client.
|
||||
pub ignore_client_order: bool,
|
||||
|
||||
/// The maximum size of TLS message we'll emit. If None, we don't limit TLS
|
||||
/// message lengths except to the 2**16 limit specified in the standard.
|
||||
///
|
||||
/// rustls enforces an arbitrary minimum of 32 bytes for this field.
|
||||
/// Out of range values are reported as errors from ServerConnection::new.
|
||||
///
|
||||
/// Setting this value to the TCP MSS may improve latency for stream-y workloads.
|
||||
pub max_fragment_size: Option<usize>,
|
||||
|
||||
/// How to store client sessions.
|
||||
pub session_storage: Arc<dyn StoresServerSessions + Send + Sync>,
|
||||
|
||||
/// How to produce tickets.
|
||||
pub ticketer: Arc<dyn ProducesTickets>,
|
||||
|
||||
/// How to choose a server cert and key.
|
||||
pub cert_resolver: Arc<dyn ResolvesServerCert>,
|
||||
|
||||
/// Protocol names we support, most preferred first.
|
||||
/// If empty we don't do ALPN at all.
|
||||
pub alpn_protocols: Vec<Vec<u8>>,
|
||||
|
||||
/// Supported protocol versions, in no particular order.
|
||||
/// The default is all supported versions.
|
||||
pub(super) versions: crate::versions::EnabledVersions,
|
||||
|
||||
/// How to verify client certificates.
|
||||
pub(super) verifier: Arc<dyn verify::ClientCertVerifier>,
|
||||
|
||||
/// How to output key material for debugging. The default
|
||||
/// does nothing.
|
||||
pub key_log: Arc<dyn KeyLog>,
|
||||
|
||||
/// Allows traffic secrets to be extracted after the handshake,
|
||||
/// e.g. for kTLS setup.
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub enable_secret_extraction: bool,
|
||||
|
||||
/// Amount of early data to accept for sessions created by
|
||||
/// this config. Specify 0 to disable early data. The
|
||||
/// default is 0.
|
||||
///
|
||||
/// Read the early data via [`ServerConnection::early_data`].
|
||||
///
|
||||
/// The units for this are _both_ plaintext bytes, _and_ ciphertext
|
||||
/// bytes, depending on whether the server accepts a client's early_data
|
||||
/// or not. It is therefore recommended to include some slop in
|
||||
/// this value to account for the unknown amount of ciphertext
|
||||
/// expansion in the latter case.
|
||||
pub max_early_data_size: u32,
|
||||
|
||||
/// Whether the server should send "0.5RTT" data. This means the server
|
||||
/// sends data after its first flight of handshake messages, without
|
||||
/// waiting for the client to complete the handshake.
|
||||
///
|
||||
/// This can improve TTFB latency for either server-speaks-first protocols,
|
||||
/// or client-speaks-first protocols when paired with "0RTT" data. This
|
||||
/// comes at the cost of a subtle weakening of the normal handshake
|
||||
/// integrity guarantees that TLS provides. Note that the initial
|
||||
/// `ClientHello` is indirectly authenticated because it is included
|
||||
/// in the transcript used to derive the keys used to encrypt the data.
|
||||
///
|
||||
/// This only applies to TLS1.3 connections. TLS1.2 connections cannot
|
||||
/// do this optimisation and this setting is ignored for them. It is
|
||||
/// also ignored for TLS1.3 connections that even attempt client
|
||||
/// authentication.
|
||||
///
|
||||
/// This defaults to false. This means the first application data
|
||||
/// sent by the server comes after receiving and validating the client's
|
||||
/// handshake up to the `Finished` message. This is the safest option.
|
||||
pub send_half_rtt_data: bool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ServerConfig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ServerConfig")
|
||||
.field("ignore_client_order", &self.ignore_client_order)
|
||||
.field("max_fragment_size", &self.max_fragment_size)
|
||||
.field("alpn_protocols", &self.alpn_protocols)
|
||||
.field("max_early_data_size", &self.max_early_data_size)
|
||||
.field("send_half_rtt_data", &self.send_half_rtt_data)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
/// Create builder to build up the server configuration.
|
||||
///
|
||||
/// For more information, see the [`ConfigBuilder`] documentation.
|
||||
pub fn builder() -> ConfigBuilder<Self, WantsCipherSuites> {
|
||||
ConfigBuilder {
|
||||
state: WantsCipherSuites(()),
|
||||
side: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
/// We support a given TLS version if it's quoted in the configured
|
||||
/// versions *and* at least one ciphersuite for this version is
|
||||
/// also configured.
|
||||
pub fn supports_version(&self, v: ProtocolVersion) -> bool {
|
||||
self.versions.contains(v)
|
||||
&& self
|
||||
.cipher_suites
|
||||
.iter()
|
||||
.any(|cs| cs.version().version == v)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows reading of early data in resumed TLS1.3 connections.
|
||||
///
|
||||
/// "Early data" is also known as "0-RTT data".
|
||||
///
|
||||
/// This structure implements [`std::io::Read`].
|
||||
pub struct ReadEarlyData<'a> {
|
||||
early_data: &'a mut EarlyDataState,
|
||||
}
|
||||
|
||||
impl<'a> ReadEarlyData<'a> {
|
||||
fn new(early_data: &'a mut EarlyDataState) -> Self {
|
||||
ReadEarlyData { early_data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::io::Read for ReadEarlyData<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.early_data.read(buf)
|
||||
}
|
||||
|
||||
#[cfg(read_buf)]
|
||||
fn read_buf(&mut self, cursor: io::BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.early_data.read_buf(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents a single TLS server connection.
|
||||
///
|
||||
/// Send TLS-protected data to the peer using the `io::Write` trait implementation.
|
||||
/// Read data from the peer using the `io::Read` trait implementation.
|
||||
pub struct ServerConnection {
|
||||
inner: ConnectionCommon<ServerConnectionData>,
|
||||
}
|
||||
|
||||
impl ServerConnection {
|
||||
/// Make a new ServerConnection. `config` controls how
|
||||
/// we behave in the TLS protocol.
|
||||
pub fn new(config: Arc<ServerConfig>) -> Result<Self, Error> {
|
||||
Self::from_config(config, vec![])
|
||||
}
|
||||
|
||||
fn from_config(
|
||||
config: Arc<ServerConfig>,
|
||||
extra_exts: Vec<ServerExtension>,
|
||||
) -> Result<Self, Error> {
|
||||
let mut common = CommonState::new(Side::Server);
|
||||
common.set_max_fragment_size(config.max_fragment_size)?;
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
{
|
||||
common.enable_secret_extraction = config.enable_secret_extraction;
|
||||
}
|
||||
Ok(Self {
|
||||
inner: ConnectionCommon::new(
|
||||
Box::new(hs::ExpectClientHello::new(config, extra_exts)),
|
||||
ServerConnectionData::default(),
|
||||
common,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieves the SNI hostname, if any, used to select the certificate and
|
||||
/// private key.
|
||||
///
|
||||
/// This returns `None` until some time after the client's SNI extension
|
||||
/// value is processed during the handshake. It will never be `None` when
|
||||
/// the connection is ready to send or process application data, unless the
|
||||
/// client does not support SNI.
|
||||
///
|
||||
/// This is useful for application protocols that need to enforce that the
|
||||
/// SNI hostname matches an application layer protocol hostname. For
|
||||
/// example, HTTP/1.1 servers commonly expect the `Host:` header field of
|
||||
/// every request on a connection to match the hostname in the SNI extension
|
||||
/// when the client provides the SNI extension.
|
||||
///
|
||||
/// The SNI hostname is also used to match sessions during session
|
||||
/// resumption.
|
||||
pub fn sni_hostname(&self) -> Option<&str> {
|
||||
self.inner.data.get_sni_str()
|
||||
}
|
||||
|
||||
/// Application-controlled portion of the resumption ticket supplied by the client, if any.
|
||||
///
|
||||
/// Recovered from the prior session's `set_resumption_data`. Integrity is guaranteed by rustls.
|
||||
///
|
||||
/// Returns `Some` iff a valid resumption ticket has been received from the client.
|
||||
pub fn received_resumption_data(&self) -> Option<&[u8]> {
|
||||
self.inner
|
||||
.data
|
||||
.received_resumption_data
|
||||
.as_ref()
|
||||
.map(|x| &x[..])
|
||||
}
|
||||
|
||||
/// Set the resumption data to embed in future resumption tickets supplied to the client.
|
||||
///
|
||||
/// Defaults to the empty byte string. Must be less than 2^15 bytes to allow room for other
|
||||
/// data. Should be called while `is_handshaking` returns true to ensure all transmitted
|
||||
/// resumption tickets are affected.
|
||||
///
|
||||
/// Integrity will be assured by rustls, but the data will be visible to the client. If secrecy
|
||||
/// from the client is desired, encrypt the data separately.
|
||||
pub fn set_resumption_data(&mut self, data: &[u8]) {
|
||||
assert!(data.len() < 2usize.pow(15));
|
||||
self.inner.data.resumption_data = data.into();
|
||||
}
|
||||
|
||||
/// Explicitly discard early data, notifying the client
|
||||
///
|
||||
/// Useful if invariants encoded in `received_resumption_data()` cannot be respected.
|
||||
///
|
||||
/// Must be called while `is_handshaking` is true.
|
||||
pub fn reject_early_data(&mut self) {
|
||||
assert!(
|
||||
self.is_handshaking(),
|
||||
"cannot retroactively reject early data"
|
||||
);
|
||||
self.inner.data.early_data.reject();
|
||||
}
|
||||
|
||||
/// Returns an `io::Read` implementer you can read bytes from that are
|
||||
/// received from a client as TLS1.3 0RTT/"early" data, during the handshake.
|
||||
///
|
||||
/// This returns `None` in many circumstances, such as :
|
||||
///
|
||||
/// - Early data is disabled if [`ServerConfig::max_early_data_size`] is zero (the default).
|
||||
/// - The session negotiated with the client is not TLS1.3.
|
||||
/// - The client just doesn't support early data.
|
||||
/// - The connection doesn't resume an existing session.
|
||||
/// - The client hasn't sent a full ClientHello yet.
|
||||
pub fn early_data(&mut self) -> Option<ReadEarlyData> {
|
||||
if self
|
||||
.inner
|
||||
.data
|
||||
.early_data
|
||||
.was_accepted()
|
||||
{
|
||||
Some(ReadEarlyData::new(&mut self.inner.data.early_data))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract secrets, so they can be used when configuring kTLS, for example.
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub fn extract_secrets(self) -> Result<ExtractedSecrets, Error> {
|
||||
self.inner.extract_secrets()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ServerConnection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("ServerConnection")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ServerConnection {
|
||||
type Target = ConnectionCommon<ServerConnectionData>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ServerConnection {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ServerConnection> for crate::Connection {
|
||||
fn from(conn: ServerConnection) -> Self {
|
||||
Self::Server(conn)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle on a server-side connection before configuration is available.
|
||||
///
|
||||
/// The `Acceptor` allows the caller to provide a [`ServerConfig`] based on the [`ClientHello`] of
|
||||
/// the incoming connection.
|
||||
pub struct Acceptor {
|
||||
inner: Option<ConnectionCommon<ServerConnectionData>>,
|
||||
}
|
||||
|
||||
impl Default for Acceptor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: Some(ConnectionCommon::new(
|
||||
Box::new(Accepting),
|
||||
ServerConnectionData::default(),
|
||||
CommonState::new(Side::Server),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Acceptor {
|
||||
/// Create a new `Acceptor`.
|
||||
#[deprecated(
|
||||
since = "0.20.7",
|
||||
note = "Use Acceptor::default instead for an infallible constructor"
|
||||
)]
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
/// Returns true if the caller should call [`Connection::read_tls()`] as soon as possible.
|
||||
///
|
||||
/// Since the purpose of an Acceptor is to read and then parse TLS bytes, this always returns true.
|
||||
///
|
||||
/// [`Connection::read_tls()`]: crate::Connection::read_tls
|
||||
#[deprecated(since = "0.20.7", note = "Always returns true")]
|
||||
pub fn wants_read(&self) -> bool {
|
||||
self.inner
|
||||
.as_ref()
|
||||
.map(|conn| conn.common_state.wants_read())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Read TLS content from `rd`.
|
||||
///
|
||||
/// Returns an error if this `Acceptor` has already yielded an [`Accepted`]. For more details,
|
||||
/// refer to [`Connection::read_tls()`].
|
||||
///
|
||||
/// [`Connection::read_tls()`]: crate::Connection::read_tls
|
||||
pub fn read_tls(&mut self, rd: &mut dyn io::Read) -> Result<usize, io::Error> {
|
||||
match &mut self.inner {
|
||||
Some(conn) => conn.read_tls(rd),
|
||||
None => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"acceptor cannot read after successful acceptance",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a `ClientHello` message has been received.
|
||||
///
|
||||
/// Returns `Ok(None)` if the complete `ClientHello` has not yet been received.
|
||||
/// Do more I/O and then call this function again.
|
||||
///
|
||||
/// Returns `Ok(Some(accepted))` if the connection has been accepted. Call
|
||||
/// `accepted.into_connection()` to continue. Do not call this function again.
|
||||
///
|
||||
/// Returns `Err(err)` if an error occurred. Do not call this function again.
|
||||
pub fn accept(&mut self) -> Result<Option<Accepted>, Error> {
|
||||
let mut connection = match self.inner.take() {
|
||||
Some(conn) => conn,
|
||||
None => {
|
||||
return Err(Error::General("Acceptor polled after completion".into()));
|
||||
}
|
||||
};
|
||||
|
||||
let message = match connection.first_handshake_message()? {
|
||||
Some(msg) => msg,
|
||||
None => {
|
||||
self.inner = Some(connection);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
let (_, sig_schemes) = hs::process_client_hello(
|
||||
&message,
|
||||
false,
|
||||
&mut connection.common_state,
|
||||
&mut connection.data,
|
||||
)?;
|
||||
|
||||
Ok(Some(Accepted {
|
||||
connection,
|
||||
message,
|
||||
sig_schemes,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a `ClientHello` message received through the [`Acceptor`].
|
||||
///
|
||||
/// Contains the state required to resume the connection through [`Accepted::into_connection()`].
|
||||
pub struct Accepted {
|
||||
connection: ConnectionCommon<ServerConnectionData>,
|
||||
message: Message,
|
||||
sig_schemes: Vec<SignatureScheme>,
|
||||
}
|
||||
|
||||
impl Accepted {
|
||||
/// Get the [`ClientHello`] for this connection.
|
||||
pub fn client_hello(&self) -> ClientHello<'_> {
|
||||
let payload = Self::client_hello_payload(&self.message);
|
||||
ClientHello::new(
|
||||
&self.connection.data.sni,
|
||||
&self.sig_schemes,
|
||||
payload.get_alpn_extension(),
|
||||
&payload.cipher_suites,
|
||||
)
|
||||
}
|
||||
|
||||
/// Convert the [`Accepted`] into a [`ServerConnection`].
|
||||
///
|
||||
/// Takes the state returned from [`Acceptor::accept()`] as well as the [`ServerConfig`] and
|
||||
/// [`sign::CertifiedKey`] that should be used for the session. Returns an error if
|
||||
/// configuration-dependent validation of the received `ClientHello` message fails.
|
||||
pub fn into_connection(mut self, config: Arc<ServerConfig>) -> Result<ServerConnection, Error> {
|
||||
self.connection
|
||||
.common_state
|
||||
.set_max_fragment_size(config.max_fragment_size)?;
|
||||
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
{
|
||||
self.connection
|
||||
.common_state
|
||||
.enable_secret_extraction = config.enable_secret_extraction;
|
||||
}
|
||||
|
||||
let state = hs::ExpectClientHello::new(config, Vec::new());
|
||||
let mut cx = hs::ServerContext {
|
||||
common: &mut self.connection.common_state,
|
||||
data: &mut self.connection.data,
|
||||
};
|
||||
|
||||
let new = state.with_certified_key(
|
||||
self.sig_schemes,
|
||||
Self::client_hello_payload(&self.message),
|
||||
&self.message,
|
||||
&mut cx,
|
||||
)?;
|
||||
|
||||
self.connection.replace_state(new);
|
||||
Ok(ServerConnection {
|
||||
inner: self.connection,
|
||||
})
|
||||
}
|
||||
|
||||
fn client_hello_payload(message: &Message) -> &ClientHelloPayload {
|
||||
match &message.payload {
|
||||
crate::msgs::message::MessagePayload::Handshake { parsed, .. } => match &parsed.payload
|
||||
{
|
||||
crate::msgs::handshake::HandshakePayload::ClientHello(ch) => ch,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Accepting;
|
||||
|
||||
impl State<ServerConnectionData> for Accepting {
|
||||
fn handle(
|
||||
self: Box<Self>,
|
||||
_cx: &mut hs::ServerContext<'_>,
|
||||
_m: Message,
|
||||
) -> Result<Box<dyn State<ServerConnectionData>>, Error> {
|
||||
Err(Error::General("unreachable state".into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum EarlyDataState {
|
||||
New,
|
||||
Accepted(ChunkVecBuffer),
|
||||
Rejected,
|
||||
}
|
||||
|
||||
impl Default for EarlyDataState {
|
||||
fn default() -> Self {
|
||||
Self::New
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyDataState {
|
||||
pub(super) fn reject(&mut self) {
|
||||
*self = Self::Rejected;
|
||||
}
|
||||
|
||||
pub(super) fn accept(&mut self, max_size: usize) {
|
||||
*self = Self::Accepted(ChunkVecBuffer::new(Some(max_size)));
|
||||
}
|
||||
|
||||
fn was_accepted(&self) -> bool {
|
||||
matches!(self, Self::Accepted(_))
|
||||
}
|
||||
|
||||
pub(super) fn was_rejected(&self) -> bool {
|
||||
matches!(self, Self::Rejected)
|
||||
}
|
||||
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self {
|
||||
Self::Accepted(ref mut received) => received.read(buf),
|
||||
_ => Err(io::Error::from(io::ErrorKind::BrokenPipe)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(read_buf)]
|
||||
fn read_buf(&mut self, cursor: io::BorrowedCursor<'_>) -> io::Result<()> {
|
||||
match self {
|
||||
Self::Accepted(ref mut received) => received.read_buf(cursor),
|
||||
_ => Err(io::Error::from(io::ErrorKind::BrokenPipe)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn take_received_plaintext(&mut self, bytes: Payload) -> bool {
|
||||
let available = bytes.0.len();
|
||||
match self {
|
||||
Self::Accepted(ref mut received) if received.apply_limit(available) == available => {
|
||||
received.append(bytes.0);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// these branches not reachable externally, unless something else goes wrong.
|
||||
#[test]
|
||||
fn test_read_in_new_state() {
|
||||
assert_eq!(
|
||||
format!("{:?}", EarlyDataState::default().read(&mut [0u8; 5])),
|
||||
"Err(Kind(BrokenPipe))"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(read_buf)]
|
||||
#[test]
|
||||
fn test_read_buf_in_new_state() {
|
||||
use std::io::BorrowedBuf;
|
||||
|
||||
let mut buf = [0u8; 5];
|
||||
let mut buf: BorrowedBuf<'_> = buf.as_mut_slice().into();
|
||||
assert_eq!(
|
||||
format!("{:?}", EarlyDataState::default().read_buf(buf.unfilled())),
|
||||
"Err(Kind(BrokenPipe))"
|
||||
);
|
||||
}
|
||||
|
||||
/// State associated with a server connection.
|
||||
#[derive(Default)]
|
||||
pub struct ServerConnectionData {
|
||||
pub(super) sni: Option<webpki::DnsName>,
|
||||
pub(super) received_resumption_data: Option<Vec<u8>>,
|
||||
pub(super) resumption_data: Vec<u8>,
|
||||
pub(super) early_data: EarlyDataState,
|
||||
}
|
||||
|
||||
impl ServerConnectionData {
|
||||
pub(super) fn get_sni_str(&self) -> Option<&str> {
|
||||
self.sni.as_ref().map(AsRef::as_ref)
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::conn::SideData for ServerConnectionData {}
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
impl quic::QuicExt for ServerConnection {
|
||||
fn quic_transport_parameters(&self) -> Option<&[u8]> {
|
||||
self.inner
|
||||
.common_state
|
||||
.quic
|
||||
.params
|
||||
.as_ref()
|
||||
.map(|v| v.as_ref())
|
||||
}
|
||||
|
||||
fn zero_rtt_keys(&self) -> Option<quic::DirectionalKeys> {
|
||||
Some(quic::DirectionalKeys::new(
|
||||
self.inner
|
||||
.common_state
|
||||
.suite
|
||||
.and_then(|suite| suite.tls13())?,
|
||||
self.inner
|
||||
.common_state
|
||||
.quic
|
||||
.early_secret
|
||||
.as_ref()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn read_hs(&mut self, plaintext: &[u8]) -> Result<(), Error> {
|
||||
self.inner.read_quic_hs(plaintext)
|
||||
}
|
||||
|
||||
fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<quic::KeyChange> {
|
||||
quic::write_hs(&mut self.inner.common_state, buf)
|
||||
}
|
||||
|
||||
fn alert(&self) -> Option<AlertDescription> {
|
||||
self.inner.common_state.quic.alert
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods specific to QUIC server sessions
|
||||
#[cfg(feature = "quic")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "quic")))]
|
||||
pub trait ServerQuicExt {
|
||||
/// Make a new QUIC ServerConnection. This differs from `ServerConnection::new()`
|
||||
/// in that it takes an extra argument, `params`, which contains the
|
||||
/// TLS-encoded transport parameters to send.
|
||||
fn new_quic(
|
||||
config: Arc<ServerConfig>,
|
||||
quic_version: quic::Version,
|
||||
params: Vec<u8>,
|
||||
) -> Result<ServerConnection, Error> {
|
||||
if !config.supports_version(ProtocolVersion::TLSv1_3) {
|
||||
return Err(Error::General(
|
||||
"TLS 1.3 support is required for QUIC".into(),
|
||||
));
|
||||
}
|
||||
|
||||
if config.max_early_data_size != 0 && config.max_early_data_size != 0xffff_ffff {
|
||||
return Err(Error::General(
|
||||
"QUIC sessions must set a max early data of 0 or 2^32-1".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let ext = match quic_version {
|
||||
quic::Version::V1Draft => ServerExtension::TransportParametersDraft(params),
|
||||
quic::Version::V1 => ServerExtension::TransportParameters(params),
|
||||
};
|
||||
let mut new = ServerConnection::from_config(config, vec![ext])?;
|
||||
new.inner.common_state.protocol = Protocol::Quic;
|
||||
Ok(new)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
impl ServerQuicExt for ServerConnection {}
|
||||
941
third_party/rustls-fork-shadow-tls/src/server/tls12.rs
vendored
Normal file
941
third_party/rustls-fork-shadow-tls/src/server/tls12.rs
vendored
Normal file
|
|
@ -0,0 +1,941 @@
|
|||
use crate::check::inappropriate_message;
|
||||
use crate::conn::{CommonState, ConnectionRandoms, Side, State};
|
||||
use crate::enums::ProtocolVersion;
|
||||
use crate::error::Error;
|
||||
use crate::hash_hs::HandshakeHash;
|
||||
use crate::key::Certificate;
|
||||
#[cfg(feature = "logging")]
|
||||
use crate::log::{debug, trace};
|
||||
use crate::msgs::base::Payload;
|
||||
use crate::msgs::ccs::ChangeCipherSpecPayload;
|
||||
use crate::msgs::codec::Codec;
|
||||
use crate::msgs::enums::{AlertDescription, ContentType, HandshakeType};
|
||||
use crate::msgs::handshake::{ClientECDHParams, HandshakeMessagePayload, HandshakePayload};
|
||||
use crate::msgs::handshake::{NewSessionTicketPayload, SessionID};
|
||||
use crate::msgs::message::{Message, MessagePayload};
|
||||
use crate::msgs::persist;
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
use crate::suites::PartiallyExtractedSecrets;
|
||||
use crate::tls12::{self, ConnectionSecrets, Tls12CipherSuite};
|
||||
use crate::{kx, ticketer, verify};
|
||||
|
||||
use super::common::ActiveCertifiedKey;
|
||||
use super::hs::{self, ServerContext};
|
||||
use super::server_conn::{ProducesTickets, ServerConfig, ServerConnectionData};
|
||||
|
||||
use ring::constant_time;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(super) use client_hello::CompleteClientHelloHandling;
|
||||
|
||||
mod client_hello {
|
||||
use crate::enums::SignatureScheme;
|
||||
use crate::msgs::enums::ECPointFormat;
|
||||
use crate::msgs::enums::{ClientCertificateType, Compression};
|
||||
use crate::msgs::handshake::{CertificateRequestPayload, ClientSessionTicket, Random};
|
||||
use crate::msgs::handshake::{
|
||||
CertificateStatus, DigitallySignedStruct, ECDHEServerKeyExchange,
|
||||
};
|
||||
use crate::msgs::handshake::{ClientExtension, SessionID};
|
||||
use crate::msgs::handshake::{ClientHelloPayload, ServerHelloPayload};
|
||||
use crate::msgs::handshake::{ECPointFormatList, ServerECDHParams, SupportedPointFormats};
|
||||
use crate::msgs::handshake::{ServerExtension, ServerKeyExchangePayload};
|
||||
use crate::sign;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(in crate::server) struct CompleteClientHelloHandling {
|
||||
pub(in crate::server) config: Arc<ServerConfig>,
|
||||
pub(in crate::server) transcript: HandshakeHash,
|
||||
pub(in crate::server) session_id: SessionID,
|
||||
pub(in crate::server) suite: &'static Tls12CipherSuite,
|
||||
pub(in crate::server) using_ems: bool,
|
||||
pub(in crate::server) randoms: ConnectionRandoms,
|
||||
pub(in crate::server) send_ticket: bool,
|
||||
pub(in crate::server) extra_exts: Vec<ServerExtension>,
|
||||
}
|
||||
|
||||
impl CompleteClientHelloHandling {
|
||||
pub(in crate::server) fn handle_client_hello(
|
||||
mut self,
|
||||
cx: &mut ServerContext<'_>,
|
||||
server_key: ActiveCertifiedKey,
|
||||
chm: &Message,
|
||||
client_hello: &ClientHelloPayload,
|
||||
sigschemes_ext: Vec<SignatureScheme>,
|
||||
tls13_enabled: bool,
|
||||
) -> hs::NextStateOrError {
|
||||
// -- TLS1.2 only from hereon in --
|
||||
self.transcript.add_message(chm);
|
||||
|
||||
if client_hello.ems_support_offered() {
|
||||
self.using_ems = true;
|
||||
}
|
||||
|
||||
let groups_ext = client_hello
|
||||
.get_namedgroups_extension()
|
||||
.ok_or_else(|| hs::incompatible(cx.common, "client didn't describe groups"))?;
|
||||
let ecpoints_ext = client_hello
|
||||
.get_ecpoints_extension()
|
||||
.ok_or_else(|| hs::incompatible(cx.common, "client didn't describe ec points"))?;
|
||||
|
||||
trace!("namedgroups {:?}", groups_ext);
|
||||
trace!("ecpoints {:?}", ecpoints_ext);
|
||||
|
||||
if !ecpoints_ext.contains(&ECPointFormat::Uncompressed) {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::IllegalParameter);
|
||||
return Err(Error::PeerIncompatibleError(
|
||||
"client didn't support uncompressed ec points".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// -- If TLS1.3 is enabled, signal the downgrade in the server random
|
||||
if tls13_enabled {
|
||||
self.randoms.server[24..].copy_from_slice(&tls12::DOWNGRADE_SENTINEL);
|
||||
}
|
||||
|
||||
// -- Check for resumption --
|
||||
// We can do this either by (in order of preference):
|
||||
// 1. receiving a ticket that decrypts
|
||||
// 2. receiving a sessionid that is in our cache
|
||||
//
|
||||
// If we receive a ticket, the sessionid won't be in our
|
||||
// cache, so don't check.
|
||||
//
|
||||
// If either works, we end up with a ServerConnectionValue
|
||||
// which is passed to start_resumption and concludes
|
||||
// our handling of the ClientHello.
|
||||
//
|
||||
let mut ticket_received = false;
|
||||
let resume_data = client_hello
|
||||
.get_ticket_extension()
|
||||
.and_then(|ticket_ext| match ticket_ext {
|
||||
ClientExtension::SessionTicket(ClientSessionTicket::Offer(ticket)) => {
|
||||
Some(ticket)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.and_then(|ticket| {
|
||||
ticket_received = true;
|
||||
debug!("Ticket received");
|
||||
let data = self.config.ticketer.decrypt(&ticket.0);
|
||||
if data.is_none() {
|
||||
debug!("Ticket didn't decrypt");
|
||||
}
|
||||
data
|
||||
})
|
||||
.or_else(|| {
|
||||
// Perhaps resume? If we received a ticket, the sessionid
|
||||
// does not correspond to a real session.
|
||||
if client_hello.session_id.is_empty() || ticket_received {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.config
|
||||
.session_storage
|
||||
.get(&client_hello.session_id.get_encoding())
|
||||
})
|
||||
.and_then(|x| persist::ServerSessionValue::read_bytes(&x))
|
||||
.filter(|resumedata| {
|
||||
hs::can_resume(self.suite.into(), &cx.data.sni, self.using_ems, resumedata)
|
||||
});
|
||||
|
||||
if let Some(data) = resume_data {
|
||||
return self.start_resumption(cx, client_hello, &client_hello.session_id, data);
|
||||
}
|
||||
|
||||
// Now we have chosen a ciphersuite, we can make kx decisions.
|
||||
let sigschemes = self
|
||||
.suite
|
||||
.resolve_sig_schemes(&sigschemes_ext);
|
||||
|
||||
if sigschemes.is_empty() {
|
||||
return Err(hs::incompatible(cx.common, "no overlapping sigschemes"));
|
||||
}
|
||||
|
||||
let group = self
|
||||
.config
|
||||
.kx_groups
|
||||
.iter()
|
||||
.find(|skxg| groups_ext.contains(&skxg.name))
|
||||
.cloned()
|
||||
.ok_or_else(|| hs::incompatible(cx.common, "no supported group"))?;
|
||||
|
||||
let ecpoint = ECPointFormatList::supported()
|
||||
.iter()
|
||||
.find(|format| ecpoints_ext.contains(format))
|
||||
.cloned()
|
||||
.ok_or_else(|| hs::incompatible(cx.common, "no supported point format"))?;
|
||||
|
||||
debug_assert_eq!(ecpoint, ECPointFormat::Uncompressed);
|
||||
|
||||
let (mut ocsp_response, mut sct_list) =
|
||||
(server_key.get_ocsp(), server_key.get_sct_list());
|
||||
|
||||
// If we're not offered a ticket or a potential session ID, allocate a session ID.
|
||||
if !self.config.session_storage.can_cache() {
|
||||
self.session_id = SessionID::empty();
|
||||
} else if self.session_id.is_empty() && !ticket_received {
|
||||
self.session_id = SessionID::random()?;
|
||||
}
|
||||
|
||||
self.send_ticket = emit_server_hello(
|
||||
&self.config,
|
||||
&mut self.transcript,
|
||||
cx,
|
||||
self.session_id,
|
||||
self.suite,
|
||||
self.using_ems,
|
||||
&mut ocsp_response,
|
||||
&mut sct_list,
|
||||
client_hello,
|
||||
None,
|
||||
&self.randoms,
|
||||
self.extra_exts,
|
||||
)?;
|
||||
emit_certificate(&mut self.transcript, cx.common, server_key.get_cert());
|
||||
if let Some(ocsp_response) = ocsp_response {
|
||||
emit_cert_status(&mut self.transcript, cx.common, ocsp_response);
|
||||
}
|
||||
let server_kx = emit_server_kx(
|
||||
&mut self.transcript,
|
||||
cx.common,
|
||||
sigschemes,
|
||||
group,
|
||||
server_key.get_key(),
|
||||
&self.randoms,
|
||||
)?;
|
||||
let doing_client_auth = emit_certificate_req(&self.config, &mut self.transcript, cx)?;
|
||||
emit_server_hello_done(&mut self.transcript, cx.common);
|
||||
|
||||
if doing_client_auth {
|
||||
Ok(Box::new(ExpectCertificate {
|
||||
config: self.config,
|
||||
transcript: self.transcript,
|
||||
randoms: self.randoms,
|
||||
session_id: self.session_id,
|
||||
suite: self.suite,
|
||||
using_ems: self.using_ems,
|
||||
server_kx,
|
||||
send_ticket: self.send_ticket,
|
||||
}))
|
||||
} else {
|
||||
Ok(Box::new(ExpectClientKx {
|
||||
config: self.config,
|
||||
transcript: self.transcript,
|
||||
randoms: self.randoms,
|
||||
session_id: self.session_id,
|
||||
suite: self.suite,
|
||||
using_ems: self.using_ems,
|
||||
server_kx,
|
||||
client_cert: None,
|
||||
send_ticket: self.send_ticket,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn start_resumption(
|
||||
mut self,
|
||||
cx: &mut ServerContext<'_>,
|
||||
client_hello: &ClientHelloPayload,
|
||||
id: &SessionID,
|
||||
resumedata: persist::ServerSessionValue,
|
||||
) -> hs::NextStateOrError {
|
||||
debug!("Resuming connection");
|
||||
|
||||
if resumedata.extended_ms && !self.using_ems {
|
||||
return Err(cx
|
||||
.common
|
||||
.illegal_param("refusing to resume without ems"));
|
||||
}
|
||||
|
||||
self.session_id = *id;
|
||||
self.send_ticket = emit_server_hello(
|
||||
&self.config,
|
||||
&mut self.transcript,
|
||||
cx,
|
||||
self.session_id,
|
||||
self.suite,
|
||||
self.using_ems,
|
||||
&mut None,
|
||||
&mut None,
|
||||
client_hello,
|
||||
Some(&resumedata),
|
||||
&self.randoms,
|
||||
self.extra_exts,
|
||||
)?;
|
||||
|
||||
let secrets = ConnectionSecrets::new_resume(
|
||||
self.randoms,
|
||||
self.suite,
|
||||
&resumedata.master_secret.0,
|
||||
);
|
||||
self.config.key_log.log(
|
||||
"CLIENT_RANDOM",
|
||||
&secrets.randoms.client,
|
||||
&secrets.master_secret,
|
||||
);
|
||||
cx.common
|
||||
.start_encryption_tls12(&secrets, Side::Server);
|
||||
cx.common.peer_certificates = resumedata.client_cert_chain;
|
||||
|
||||
if self.send_ticket {
|
||||
emit_ticket(
|
||||
&secrets,
|
||||
&mut self.transcript,
|
||||
self.using_ems,
|
||||
cx,
|
||||
&*self.config.ticketer,
|
||||
)?;
|
||||
}
|
||||
emit_ccs(cx.common);
|
||||
cx.common
|
||||
.record_layer
|
||||
.start_encrypting();
|
||||
emit_finished(&secrets, &mut self.transcript, cx.common);
|
||||
|
||||
Ok(Box::new(ExpectCcs {
|
||||
config: self.config,
|
||||
secrets,
|
||||
transcript: self.transcript,
|
||||
session_id: self.session_id,
|
||||
using_ems: self.using_ems,
|
||||
resuming: true,
|
||||
send_ticket: self.send_ticket,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_server_hello(
|
||||
config: &ServerConfig,
|
||||
transcript: &mut HandshakeHash,
|
||||
cx: &mut ServerContext<'_>,
|
||||
session_id: SessionID,
|
||||
suite: &'static Tls12CipherSuite,
|
||||
using_ems: bool,
|
||||
ocsp_response: &mut Option<&[u8]>,
|
||||
sct_list: &mut Option<&[u8]>,
|
||||
hello: &ClientHelloPayload,
|
||||
resumedata: Option<&persist::ServerSessionValue>,
|
||||
randoms: &ConnectionRandoms,
|
||||
extra_exts: Vec<ServerExtension>,
|
||||
) -> Result<bool, Error> {
|
||||
let mut ep = hs::ExtensionProcessing::new();
|
||||
ep.process_common(
|
||||
config,
|
||||
cx,
|
||||
ocsp_response,
|
||||
sct_list,
|
||||
hello,
|
||||
resumedata,
|
||||
extra_exts,
|
||||
)?;
|
||||
ep.process_tls12(config, hello, using_ems);
|
||||
|
||||
let sh = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::ServerHello,
|
||||
payload: HandshakePayload::ServerHello(ServerHelloPayload {
|
||||
legacy_version: ProtocolVersion::TLSv1_2,
|
||||
random: Random::from(randoms.server),
|
||||
session_id,
|
||||
cipher_suite: suite.common.suite,
|
||||
compression_method: Compression::Null,
|
||||
extensions: ep.exts,
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
||||
trace!("sending server hello {:?}", sh);
|
||||
transcript.add_message(&sh);
|
||||
cx.common.send_msg(sh, false);
|
||||
Ok(ep.send_ticket)
|
||||
}
|
||||
|
||||
fn emit_certificate(
|
||||
transcript: &mut HandshakeHash,
|
||||
common: &mut CommonState,
|
||||
cert_chain: &[Certificate],
|
||||
) {
|
||||
let c = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::Certificate,
|
||||
payload: HandshakePayload::Certificate(cert_chain.to_owned()),
|
||||
}),
|
||||
};
|
||||
|
||||
transcript.add_message(&c);
|
||||
common.send_msg(c, false);
|
||||
}
|
||||
|
||||
fn emit_cert_status(transcript: &mut HandshakeHash, common: &mut CommonState, ocsp: &[u8]) {
|
||||
let st = CertificateStatus::new(ocsp.to_owned());
|
||||
|
||||
let c = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::CertificateStatus,
|
||||
payload: HandshakePayload::CertificateStatus(st),
|
||||
}),
|
||||
};
|
||||
|
||||
transcript.add_message(&c);
|
||||
common.send_msg(c, false);
|
||||
}
|
||||
|
||||
fn emit_server_kx(
|
||||
transcript: &mut HandshakeHash,
|
||||
common: &mut CommonState,
|
||||
sigschemes: Vec<SignatureScheme>,
|
||||
skxg: &'static kx::SupportedKxGroup,
|
||||
signing_key: &dyn sign::SigningKey,
|
||||
randoms: &ConnectionRandoms,
|
||||
) -> Result<kx::KeyExchange, Error> {
|
||||
let kx = kx::KeyExchange::start(skxg).ok_or(Error::FailedToGetRandomBytes)?;
|
||||
let secdh = ServerECDHParams::new(skxg.name, kx.pubkey.as_ref());
|
||||
|
||||
let mut msg = Vec::new();
|
||||
msg.extend(randoms.client);
|
||||
msg.extend(randoms.server);
|
||||
secdh.encode(&mut msg);
|
||||
|
||||
let signer = signing_key
|
||||
.choose_scheme(&sigschemes)
|
||||
.ok_or_else(|| Error::General("incompatible signing key".to_string()))?;
|
||||
let sigscheme = signer.scheme();
|
||||
let sig = signer.sign(&msg)?;
|
||||
|
||||
let skx = ServerKeyExchangePayload::ECDHE(ECDHEServerKeyExchange {
|
||||
params: secdh,
|
||||
dss: DigitallySignedStruct::new(sigscheme, sig),
|
||||
});
|
||||
|
||||
let m = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::ServerKeyExchange,
|
||||
payload: HandshakePayload::ServerKeyExchange(skx),
|
||||
}),
|
||||
};
|
||||
|
||||
transcript.add_message(&m);
|
||||
common.send_msg(m, false);
|
||||
Ok(kx)
|
||||
}
|
||||
|
||||
fn emit_certificate_req(
|
||||
config: &ServerConfig,
|
||||
transcript: &mut HandshakeHash,
|
||||
cx: &mut ServerContext<'_>,
|
||||
) -> Result<bool, Error> {
|
||||
let client_auth = &config.verifier;
|
||||
|
||||
if !client_auth.offer_client_auth() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let verify_schemes = client_auth.supported_verify_schemes();
|
||||
|
||||
let names = client_auth
|
||||
.client_auth_root_subjects()
|
||||
.ok_or_else(|| {
|
||||
debug!("could not determine root subjects based on SNI");
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::AccessDenied);
|
||||
Error::General("client rejected by client_auth_root_subjects".into())
|
||||
})?;
|
||||
|
||||
let cr = CertificateRequestPayload {
|
||||
certtypes: vec![
|
||||
ClientCertificateType::RSASign,
|
||||
ClientCertificateType::ECDSASign,
|
||||
],
|
||||
sigschemes: verify_schemes,
|
||||
canames: names,
|
||||
};
|
||||
|
||||
let m = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::CertificateRequest,
|
||||
payload: HandshakePayload::CertificateRequest(cr),
|
||||
}),
|
||||
};
|
||||
|
||||
trace!("Sending CertificateRequest {:?}", m);
|
||||
transcript.add_message(&m);
|
||||
cx.common.send_msg(m, false);
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn emit_server_hello_done(transcript: &mut HandshakeHash, common: &mut CommonState) {
|
||||
let m = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::ServerHelloDone,
|
||||
payload: HandshakePayload::ServerHelloDone,
|
||||
}),
|
||||
};
|
||||
|
||||
transcript.add_message(&m);
|
||||
common.send_msg(m, false);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Process client's Certificate for client auth ---
|
||||
struct ExpectCertificate {
|
||||
config: Arc<ServerConfig>,
|
||||
transcript: HandshakeHash,
|
||||
randoms: ConnectionRandoms,
|
||||
session_id: SessionID,
|
||||
suite: &'static Tls12CipherSuite,
|
||||
using_ems: bool,
|
||||
server_kx: kx::KeyExchange,
|
||||
send_ticket: bool,
|
||||
}
|
||||
|
||||
impl State<ServerConnectionData> for ExpectCertificate {
|
||||
fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
|
||||
self.transcript.add_message(&m);
|
||||
let cert_chain = require_handshake_msg_move!(
|
||||
m,
|
||||
HandshakeType::Certificate,
|
||||
HandshakePayload::Certificate
|
||||
)?;
|
||||
|
||||
// If we can't determine if the auth is mandatory, abort
|
||||
let mandatory = self
|
||||
.config
|
||||
.verifier
|
||||
.client_auth_mandatory()
|
||||
.ok_or_else(|| {
|
||||
debug!("could not determine if client auth is mandatory based on SNI");
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::AccessDenied);
|
||||
Error::General("client rejected by client_auth_mandatory".into())
|
||||
})?;
|
||||
|
||||
trace!("certs {:?}", cert_chain);
|
||||
|
||||
let client_cert = match cert_chain.split_first() {
|
||||
None if mandatory => {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::CertificateRequired);
|
||||
return Err(Error::NoCertificatesPresented);
|
||||
}
|
||||
None => {
|
||||
debug!("client auth requested but no certificate supplied");
|
||||
self.transcript.abandon_client_auth();
|
||||
None
|
||||
}
|
||||
Some((end_entity, intermediates)) => {
|
||||
let now = std::time::SystemTime::now();
|
||||
self.config
|
||||
.verifier
|
||||
.verify_client_cert(end_entity, intermediates, now)
|
||||
.map_err(|err| {
|
||||
hs::incompatible(cx.common, "certificate invalid");
|
||||
err
|
||||
})?;
|
||||
|
||||
Some(cert_chain)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Box::new(ExpectClientKx {
|
||||
config: self.config,
|
||||
transcript: self.transcript,
|
||||
randoms: self.randoms,
|
||||
session_id: self.session_id,
|
||||
suite: self.suite,
|
||||
using_ems: self.using_ems,
|
||||
server_kx: self.server_kx,
|
||||
client_cert,
|
||||
send_ticket: self.send_ticket,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Process client's KeyExchange ---
|
||||
struct ExpectClientKx {
|
||||
config: Arc<ServerConfig>,
|
||||
transcript: HandshakeHash,
|
||||
randoms: ConnectionRandoms,
|
||||
session_id: SessionID,
|
||||
suite: &'static Tls12CipherSuite,
|
||||
using_ems: bool,
|
||||
server_kx: kx::KeyExchange,
|
||||
client_cert: Option<Vec<Certificate>>,
|
||||
send_ticket: bool,
|
||||
}
|
||||
|
||||
impl State<ServerConnectionData> for ExpectClientKx {
|
||||
fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
|
||||
let client_kx = require_handshake_msg!(
|
||||
m,
|
||||
HandshakeType::ClientKeyExchange,
|
||||
HandshakePayload::ClientKeyExchange
|
||||
)?;
|
||||
self.transcript.add_message(&m);
|
||||
let ems_seed = self
|
||||
.using_ems
|
||||
.then(|| self.transcript.get_current_hash());
|
||||
|
||||
// Complete key agreement, and set up encryption with the
|
||||
// resulting premaster secret.
|
||||
let peer_kx_params =
|
||||
tls12::decode_ecdh_params::<ClientECDHParams>(cx.common, &client_kx.0)?;
|
||||
let secrets = ConnectionSecrets::from_key_exchange(
|
||||
self.server_kx,
|
||||
&peer_kx_params.public.0,
|
||||
ems_seed,
|
||||
self.randoms,
|
||||
self.suite,
|
||||
)?;
|
||||
|
||||
self.config.key_log.log(
|
||||
"CLIENT_RANDOM",
|
||||
&secrets.randoms.client,
|
||||
&secrets.master_secret,
|
||||
);
|
||||
cx.common
|
||||
.start_encryption_tls12(&secrets, Side::Server);
|
||||
|
||||
if let Some(client_cert) = self.client_cert {
|
||||
Ok(Box::new(ExpectCertificateVerify {
|
||||
config: self.config,
|
||||
secrets,
|
||||
transcript: self.transcript,
|
||||
session_id: self.session_id,
|
||||
using_ems: self.using_ems,
|
||||
client_cert,
|
||||
send_ticket: self.send_ticket,
|
||||
}))
|
||||
} else {
|
||||
Ok(Box::new(ExpectCcs {
|
||||
config: self.config,
|
||||
secrets,
|
||||
transcript: self.transcript,
|
||||
session_id: self.session_id,
|
||||
using_ems: self.using_ems,
|
||||
resuming: false,
|
||||
send_ticket: self.send_ticket,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Process client's certificate proof ---
|
||||
struct ExpectCertificateVerify {
|
||||
config: Arc<ServerConfig>,
|
||||
secrets: ConnectionSecrets,
|
||||
transcript: HandshakeHash,
|
||||
session_id: SessionID,
|
||||
using_ems: bool,
|
||||
client_cert: Vec<Certificate>,
|
||||
send_ticket: bool,
|
||||
}
|
||||
|
||||
impl State<ServerConnectionData> for ExpectCertificateVerify {
|
||||
fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
|
||||
let rc = {
|
||||
let sig = require_handshake_msg!(
|
||||
m,
|
||||
HandshakeType::CertificateVerify,
|
||||
HandshakePayload::CertificateVerify
|
||||
)?;
|
||||
|
||||
match self.transcript.take_handshake_buf() {
|
||||
Some(msgs) => {
|
||||
let certs = &self.client_cert;
|
||||
self.config
|
||||
.verifier
|
||||
.verify_tls12_signature(&msgs, &certs[0], sig)
|
||||
}
|
||||
None => {
|
||||
// This should be unreachable; the handshake buffer was initialized with
|
||||
// client authentication if the verifier wants to offer it.
|
||||
// `transcript.abandon_client_auth()` can extract it, but its only caller in
|
||||
// this flow will also set `ExpectClientKx::client_cert` to `None`, making it
|
||||
// impossible to reach this state.
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::AccessDenied);
|
||||
Err(Error::General("client authentication not set up".into()))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = rc {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::AccessDenied);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
trace!("client CertificateVerify OK");
|
||||
cx.common.peer_certificates = Some(self.client_cert);
|
||||
|
||||
self.transcript.add_message(&m);
|
||||
Ok(Box::new(ExpectCcs {
|
||||
config: self.config,
|
||||
secrets: self.secrets,
|
||||
transcript: self.transcript,
|
||||
session_id: self.session_id,
|
||||
using_ems: self.using_ems,
|
||||
resuming: false,
|
||||
send_ticket: self.send_ticket,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Process client's ChangeCipherSpec ---
|
||||
struct ExpectCcs {
|
||||
config: Arc<ServerConfig>,
|
||||
secrets: ConnectionSecrets,
|
||||
transcript: HandshakeHash,
|
||||
session_id: SessionID,
|
||||
using_ems: bool,
|
||||
resuming: bool,
|
||||
send_ticket: bool,
|
||||
}
|
||||
|
||||
impl State<ServerConnectionData> for ExpectCcs {
|
||||
fn handle(self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
|
||||
match m.payload {
|
||||
MessagePayload::ChangeCipherSpec(..) => {}
|
||||
payload => {
|
||||
return Err(inappropriate_message(
|
||||
&payload,
|
||||
&[ContentType::ChangeCipherSpec],
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// CCS should not be received interleaved with fragmented handshake-level
|
||||
// message.
|
||||
cx.common.check_aligned_handshake()?;
|
||||
|
||||
cx.common
|
||||
.record_layer
|
||||
.start_decrypting();
|
||||
Ok(Box::new(ExpectFinished {
|
||||
config: self.config,
|
||||
secrets: self.secrets,
|
||||
transcript: self.transcript,
|
||||
session_id: self.session_id,
|
||||
using_ems: self.using_ems,
|
||||
resuming: self.resuming,
|
||||
send_ticket: self.send_ticket,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Process client's Finished ---
|
||||
fn get_server_connection_value_tls12(
|
||||
secrets: &ConnectionSecrets,
|
||||
using_ems: bool,
|
||||
cx: &ServerContext<'_>,
|
||||
time_now: ticketer::TimeBase,
|
||||
) -> persist::ServerSessionValue {
|
||||
let version = ProtocolVersion::TLSv1_2;
|
||||
let secret = secrets.get_master_secret();
|
||||
|
||||
let mut v = persist::ServerSessionValue::new(
|
||||
cx.data.sni.as_ref(),
|
||||
version,
|
||||
secrets.suite().common.suite,
|
||||
secret,
|
||||
cx.common.peer_certificates.clone(),
|
||||
cx.common.alpn_protocol.clone(),
|
||||
cx.data.resumption_data.clone(),
|
||||
time_now,
|
||||
0,
|
||||
);
|
||||
|
||||
if using_ems {
|
||||
v.set_extended_ms_used();
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
fn emit_ticket(
|
||||
secrets: &ConnectionSecrets,
|
||||
transcript: &mut HandshakeHash,
|
||||
using_ems: bool,
|
||||
cx: &mut ServerContext<'_>,
|
||||
ticketer: &dyn ProducesTickets,
|
||||
) -> Result<(), Error> {
|
||||
let time_now = ticketer::TimeBase::now()?;
|
||||
let plain = get_server_connection_value_tls12(secrets, using_ems, cx, time_now).get_encoding();
|
||||
|
||||
// If we can't produce a ticket for some reason, we can't
|
||||
// report an error. Send an empty one.
|
||||
let ticket = ticketer
|
||||
.encrypt(&plain)
|
||||
.unwrap_or_default();
|
||||
let ticket_lifetime = ticketer.lifetime();
|
||||
|
||||
let m = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::NewSessionTicket,
|
||||
payload: HandshakePayload::NewSessionTicket(NewSessionTicketPayload::new(
|
||||
ticket_lifetime,
|
||||
ticket,
|
||||
)),
|
||||
}),
|
||||
};
|
||||
|
||||
transcript.add_message(&m);
|
||||
cx.common.send_msg(m, false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn emit_ccs(common: &mut CommonState) {
|
||||
let m = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}),
|
||||
};
|
||||
|
||||
common.send_msg(m, false);
|
||||
}
|
||||
|
||||
fn emit_finished(
|
||||
secrets: &ConnectionSecrets,
|
||||
transcript: &mut HandshakeHash,
|
||||
common: &mut CommonState,
|
||||
) {
|
||||
let vh = transcript.get_current_hash();
|
||||
let verify_data = secrets.server_verify_data(&vh);
|
||||
let verify_data_payload = Payload::new(verify_data);
|
||||
|
||||
let f = Message {
|
||||
version: ProtocolVersion::TLSv1_2,
|
||||
payload: MessagePayload::handshake(HandshakeMessagePayload {
|
||||
typ: HandshakeType::Finished,
|
||||
payload: HandshakePayload::Finished(verify_data_payload),
|
||||
}),
|
||||
};
|
||||
|
||||
transcript.add_message(&f);
|
||||
common.send_msg(f, true);
|
||||
}
|
||||
|
||||
struct ExpectFinished {
|
||||
config: Arc<ServerConfig>,
|
||||
secrets: ConnectionSecrets,
|
||||
transcript: HandshakeHash,
|
||||
session_id: SessionID,
|
||||
using_ems: bool,
|
||||
resuming: bool,
|
||||
send_ticket: bool,
|
||||
}
|
||||
|
||||
impl State<ServerConnectionData> for ExpectFinished {
|
||||
fn handle(mut self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
|
||||
let finished =
|
||||
require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?;
|
||||
|
||||
cx.common.check_aligned_handshake()?;
|
||||
|
||||
let vh = self.transcript.get_current_hash();
|
||||
let expect_verify_data = self.secrets.client_verify_data(&vh);
|
||||
|
||||
let _fin_verified =
|
||||
constant_time::verify_slices_are_equal(&expect_verify_data, &finished.0)
|
||||
.map_err(|_| {
|
||||
cx.common
|
||||
.send_fatal_alert(AlertDescription::DecryptError);
|
||||
Error::DecryptError
|
||||
})
|
||||
.map(|_| verify::FinishedMessageVerified::assertion())?;
|
||||
|
||||
// Save connection, perhaps
|
||||
if !self.resuming && !self.session_id.is_empty() {
|
||||
let time_now = ticketer::TimeBase::now()?;
|
||||
let value =
|
||||
get_server_connection_value_tls12(&self.secrets, self.using_ems, cx, time_now);
|
||||
|
||||
let worked = self
|
||||
.config
|
||||
.session_storage
|
||||
.put(self.session_id.get_encoding(), value.get_encoding());
|
||||
if worked {
|
||||
debug!("Session saved");
|
||||
} else {
|
||||
debug!("Session not saved");
|
||||
}
|
||||
}
|
||||
|
||||
// Send our CCS and Finished.
|
||||
self.transcript.add_message(&m);
|
||||
if !self.resuming {
|
||||
if self.send_ticket {
|
||||
emit_ticket(
|
||||
&self.secrets,
|
||||
&mut self.transcript,
|
||||
self.using_ems,
|
||||
cx,
|
||||
&*self.config.ticketer,
|
||||
)?;
|
||||
}
|
||||
emit_ccs(cx.common);
|
||||
cx.common
|
||||
.record_layer
|
||||
.start_encrypting();
|
||||
emit_finished(&self.secrets, &mut self.transcript, cx.common);
|
||||
}
|
||||
|
||||
cx.common.start_traffic();
|
||||
Ok(Box::new(ExpectTraffic {
|
||||
secrets: self.secrets,
|
||||
_fin_verified,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Process traffic ---
|
||||
struct ExpectTraffic {
|
||||
secrets: ConnectionSecrets,
|
||||
_fin_verified: verify::FinishedMessageVerified,
|
||||
}
|
||||
|
||||
impl ExpectTraffic {}
|
||||
|
||||
impl State<ServerConnectionData> for ExpectTraffic {
|
||||
fn handle(self: Box<Self>, cx: &mut ServerContext<'_>, m: Message) -> hs::NextStateOrError {
|
||||
match m.payload {
|
||||
MessagePayload::ApplicationData(payload) => cx
|
||||
.common
|
||||
.take_received_plaintext(payload),
|
||||
payload => {
|
||||
return Err(inappropriate_message(
|
||||
&payload,
|
||||
&[ContentType::ApplicationData],
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn export_keying_material(
|
||||
&self,
|
||||
output: &mut [u8],
|
||||
label: &[u8],
|
||||
context: Option<&[u8]>,
|
||||
) -> Result<(), Error> {
|
||||
self.secrets
|
||||
.export_keying_material(output, label, context);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> {
|
||||
self.secrets
|
||||
.extract_secrets(Side::Server)
|
||||
}
|
||||
}
|
||||
1386
third_party/rustls-fork-shadow-tls/src/server/tls13.rs
vendored
Normal file
1386
third_party/rustls-fork-shadow-tls/src/server/tls13.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
523
third_party/rustls-fork-shadow-tls/src/sign.rs
vendored
Normal file
523
third_party/rustls-fork-shadow-tls/src/sign.rs
vendored
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
use crate::enums::SignatureScheme;
|
||||
use crate::error::Error;
|
||||
use crate::key;
|
||||
use crate::msgs::enums::SignatureAlgorithm;
|
||||
use crate::x509::{wrap_in_asn1_len, wrap_in_sequence};
|
||||
|
||||
use ring::io::der;
|
||||
use ring::signature::{self, EcdsaKeyPair, Ed25519KeyPair, RsaKeyPair};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An abstract signing key.
|
||||
pub trait SigningKey: Send + Sync {
|
||||
/// Choose a `SignatureScheme` from those offered.
|
||||
///
|
||||
/// Expresses the choice by returning something that implements `Signer`,
|
||||
/// using the chosen scheme.
|
||||
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>>;
|
||||
|
||||
/// What kind of key we have.
|
||||
fn algorithm(&self) -> SignatureAlgorithm;
|
||||
}
|
||||
|
||||
/// A thing that can sign a message.
|
||||
pub trait Signer: Send + Sync {
|
||||
/// Signs `message` using the selected scheme.
|
||||
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error>;
|
||||
|
||||
/// Reveals which scheme will be used when you call `sign()`.
|
||||
fn scheme(&self) -> SignatureScheme;
|
||||
}
|
||||
|
||||
/// A packaged-together certificate chain, matching `SigningKey` and
|
||||
/// optional stapled OCSP response and/or SCT list.
|
||||
#[derive(Clone)]
|
||||
pub struct CertifiedKey {
|
||||
/// The certificate chain.
|
||||
pub cert: Vec<key::Certificate>,
|
||||
|
||||
/// The certified key.
|
||||
pub key: Arc<dyn SigningKey>,
|
||||
|
||||
/// An optional OCSP response from the certificate issuer,
|
||||
/// attesting to its continued validity.
|
||||
pub ocsp: Option<Vec<u8>>,
|
||||
|
||||
/// An optional collection of SCTs from CT logs, proving the
|
||||
/// certificate is included on those logs. This must be
|
||||
/// a `SignedCertificateTimestampList` encoding; see RFC6962.
|
||||
pub sct_list: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl CertifiedKey {
|
||||
/// Make a new CertifiedKey, with the given chain and key.
|
||||
///
|
||||
/// The cert chain must not be empty. The first certificate in the chain
|
||||
/// must be the end-entity certificate.
|
||||
pub fn new(cert: Vec<key::Certificate>, key: Arc<dyn SigningKey>) -> Self {
|
||||
Self {
|
||||
cert,
|
||||
key,
|
||||
ocsp: None,
|
||||
sct_list: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The end-entity certificate.
|
||||
pub fn end_entity_cert(&self) -> Result<&key::Certificate, SignError> {
|
||||
self.cert.get(0).ok_or(SignError(()))
|
||||
}
|
||||
|
||||
/// Check the certificate chain for validity:
|
||||
/// - it should be non-empty list
|
||||
/// - the first certificate should be parsable as a x509v3,
|
||||
/// - the first certificate should quote the given server name
|
||||
/// (if provided)
|
||||
///
|
||||
/// These checks are not security-sensitive. They are the
|
||||
/// *server* attempting to detect accidental misconfiguration.
|
||||
pub(crate) fn cross_check_end_entity_cert(
|
||||
&self,
|
||||
name: Option<webpki::DnsNameRef>,
|
||||
) -> Result<(), Error> {
|
||||
// Always reject an empty certificate chain.
|
||||
let end_entity_cert = self
|
||||
.end_entity_cert()
|
||||
.map_err(|SignError(())| {
|
||||
Error::General("No end-entity certificate in certificate chain".to_string())
|
||||
})?;
|
||||
|
||||
// Reject syntactically-invalid end-entity certificates.
|
||||
let end_entity_cert =
|
||||
webpki::EndEntityCert::try_from(end_entity_cert.as_ref()).map_err(|_| {
|
||||
Error::General(
|
||||
"End-entity certificate in certificate \
|
||||
chain is syntactically invalid"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if let Some(name) = name {
|
||||
// If SNI was offered then the certificate must be valid for
|
||||
// that hostname. Note that this doesn't fully validate that the
|
||||
// certificate is valid; it only validates that the name is one
|
||||
// that the certificate is valid for, if the certificate is
|
||||
// valid.
|
||||
if end_entity_cert
|
||||
.verify_is_valid_for_dns_name(name)
|
||||
.is_err()
|
||||
{
|
||||
return Err(Error::General(
|
||||
"The server certificate is not \
|
||||
valid for the given name"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse `der` as any supported key encoding/type, returning
|
||||
/// the first which works.
|
||||
pub fn any_supported_type(der: &key::PrivateKey) -> Result<Arc<dyn SigningKey>, SignError> {
|
||||
if let Ok(rsa) = RsaSigningKey::new(der) {
|
||||
Ok(Arc::new(rsa))
|
||||
} else if let Ok(ecdsa) = any_ecdsa_type(der) {
|
||||
Ok(ecdsa)
|
||||
} else {
|
||||
any_eddsa_type(der)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse `der` as any ECDSA key type, returning the first which works.
|
||||
///
|
||||
/// Both SEC1 (PEM section starting with 'BEGIN EC PRIVATE KEY') and PKCS8
|
||||
/// (PEM section starting with 'BEGIN PRIVATE KEY') encodings are supported.
|
||||
pub fn any_ecdsa_type(der: &key::PrivateKey) -> Result<Arc<dyn SigningKey>, SignError> {
|
||||
if let Ok(ecdsa_p256) = EcdsaSigningKey::new(
|
||||
der,
|
||||
SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||
&signature::ECDSA_P256_SHA256_ASN1_SIGNING,
|
||||
) {
|
||||
return Ok(Arc::new(ecdsa_p256));
|
||||
}
|
||||
|
||||
if let Ok(ecdsa_p384) = EcdsaSigningKey::new(
|
||||
der,
|
||||
SignatureScheme::ECDSA_NISTP384_SHA384,
|
||||
&signature::ECDSA_P384_SHA384_ASN1_SIGNING,
|
||||
) {
|
||||
return Ok(Arc::new(ecdsa_p384));
|
||||
}
|
||||
|
||||
Err(SignError(()))
|
||||
}
|
||||
|
||||
/// Parse `der` as any EdDSA key type, returning the first which works.
|
||||
pub fn any_eddsa_type(der: &key::PrivateKey) -> Result<Arc<dyn SigningKey>, SignError> {
|
||||
if let Ok(ed25519) = Ed25519SigningKey::new(der, SignatureScheme::ED25519) {
|
||||
return Ok(Arc::new(ed25519));
|
||||
}
|
||||
|
||||
// TODO: Add support for Ed448
|
||||
|
||||
Err(SignError(()))
|
||||
}
|
||||
|
||||
/// A `SigningKey` for RSA-PKCS1 or RSA-PSS.
|
||||
///
|
||||
/// This is used by the test suite, so it must be `pub`, but it isn't part of
|
||||
/// the public, stable, API.
|
||||
#[doc(hidden)]
|
||||
pub struct RsaSigningKey {
|
||||
key: Arc<RsaKeyPair>,
|
||||
}
|
||||
|
||||
static ALL_RSA_SCHEMES: &[SignatureScheme] = &[
|
||||
SignatureScheme::RSA_PSS_SHA512,
|
||||
SignatureScheme::RSA_PSS_SHA384,
|
||||
SignatureScheme::RSA_PSS_SHA256,
|
||||
SignatureScheme::RSA_PKCS1_SHA512,
|
||||
SignatureScheme::RSA_PKCS1_SHA384,
|
||||
SignatureScheme::RSA_PKCS1_SHA256,
|
||||
];
|
||||
|
||||
impl RsaSigningKey {
|
||||
/// Make a new `RsaSigningKey` from a DER encoding, in either
|
||||
/// PKCS#1 or PKCS#8 format.
|
||||
pub fn new(der: &key::PrivateKey) -> Result<Self, SignError> {
|
||||
RsaKeyPair::from_der(&der.0)
|
||||
.or_else(|_| RsaKeyPair::from_pkcs8(&der.0))
|
||||
.map(|s| Self { key: Arc::new(s) })
|
||||
.map_err(|_| SignError(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningKey for RsaSigningKey {
|
||||
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
|
||||
ALL_RSA_SCHEMES
|
||||
.iter()
|
||||
.find(|scheme| offered.contains(scheme))
|
||||
.map(|scheme| RsaSigner::new(Arc::clone(&self.key), *scheme))
|
||||
}
|
||||
|
||||
fn algorithm(&self) -> SignatureAlgorithm {
|
||||
SignatureAlgorithm::RSA
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.20.0", note = "Use RsaSigningKey")]
|
||||
pub type RSASigningKey = RsaSigningKey;
|
||||
|
||||
struct RsaSigner {
|
||||
key: Arc<RsaKeyPair>,
|
||||
scheme: SignatureScheme,
|
||||
encoding: &'static dyn signature::RsaEncoding,
|
||||
}
|
||||
|
||||
impl RsaSigner {
|
||||
fn new(key: Arc<RsaKeyPair>, scheme: SignatureScheme) -> Box<dyn Signer> {
|
||||
let encoding: &dyn signature::RsaEncoding = match scheme {
|
||||
SignatureScheme::RSA_PKCS1_SHA256 => &signature::RSA_PKCS1_SHA256,
|
||||
SignatureScheme::RSA_PKCS1_SHA384 => &signature::RSA_PKCS1_SHA384,
|
||||
SignatureScheme::RSA_PKCS1_SHA512 => &signature::RSA_PKCS1_SHA512,
|
||||
SignatureScheme::RSA_PSS_SHA256 => &signature::RSA_PSS_SHA256,
|
||||
SignatureScheme::RSA_PSS_SHA384 => &signature::RSA_PSS_SHA384,
|
||||
SignatureScheme::RSA_PSS_SHA512 => &signature::RSA_PSS_SHA512,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Box::new(Self {
|
||||
key,
|
||||
scheme,
|
||||
encoding,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Signer for RsaSigner {
|
||||
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let mut sig = vec![0; self.key.public_modulus_len()];
|
||||
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
self.key
|
||||
.sign(self.encoding, &rng, message, &mut sig)
|
||||
.map(|_| sig)
|
||||
.map_err(|_| Error::General("signing failed".to_string()))
|
||||
}
|
||||
|
||||
fn scheme(&self) -> SignatureScheme {
|
||||
self.scheme
|
||||
}
|
||||
}
|
||||
|
||||
/// A SigningKey that uses exactly one TLS-level SignatureScheme
|
||||
/// and one ring-level signature::SigningAlgorithm.
|
||||
///
|
||||
/// Compare this to RsaSigningKey, which for a particular key is
|
||||
/// willing to sign with several algorithms. This is quite poor
|
||||
/// cryptography practice, but is necessary because a given RSA key
|
||||
/// is expected to work in TLS1.2 (PKCS#1 signatures) and TLS1.3
|
||||
/// (PSS signatures) -- nobody is willing to obtain certificates for
|
||||
/// different protocol versions.
|
||||
///
|
||||
/// Currently this is only implemented for ECDSA keys.
|
||||
struct EcdsaSigningKey {
|
||||
key: Arc<EcdsaKeyPair>,
|
||||
scheme: SignatureScheme,
|
||||
}
|
||||
|
||||
impl EcdsaSigningKey {
|
||||
/// Make a new `ECDSASigningKey` from a DER encoding in PKCS#8 or SEC1
|
||||
/// format, expecting a key usable with precisely the given signature
|
||||
/// scheme.
|
||||
fn new(
|
||||
der: &key::PrivateKey,
|
||||
scheme: SignatureScheme,
|
||||
sigalg: &'static signature::EcdsaSigningAlgorithm,
|
||||
) -> Result<Self, ()> {
|
||||
EcdsaKeyPair::from_pkcs8(sigalg, &der.0)
|
||||
.map_err(|_| ())
|
||||
.or_else(|_| Self::convert_sec1_to_pkcs8(scheme, sigalg, &der.0))
|
||||
.map(|kp| Self {
|
||||
key: Arc::new(kp),
|
||||
scheme,
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert a SEC1 encoding to PKCS8, and ask ring to parse it. This
|
||||
/// can be removed once https://github.com/briansmith/ring/pull/1456
|
||||
/// (or equivalent) is landed.
|
||||
fn convert_sec1_to_pkcs8(
|
||||
scheme: SignatureScheme,
|
||||
sigalg: &'static signature::EcdsaSigningAlgorithm,
|
||||
maybe_sec1_der: &[u8],
|
||||
) -> Result<EcdsaKeyPair, ()> {
|
||||
let pkcs8_prefix = match scheme {
|
||||
SignatureScheme::ECDSA_NISTP256_SHA256 => &PKCS8_PREFIX_ECDSA_NISTP256,
|
||||
SignatureScheme::ECDSA_NISTP384_SHA384 => &PKCS8_PREFIX_ECDSA_NISTP384,
|
||||
_ => unreachable!(), // all callers are in this file
|
||||
};
|
||||
|
||||
// wrap sec1 encoding in an OCTET STRING
|
||||
let mut sec1_wrap = Vec::with_capacity(maybe_sec1_der.len() + 8);
|
||||
sec1_wrap.extend_from_slice(maybe_sec1_der);
|
||||
wrap_in_asn1_len(&mut sec1_wrap);
|
||||
sec1_wrap.insert(0, der::Tag::OctetString as u8);
|
||||
|
||||
let mut pkcs8 = Vec::with_capacity(pkcs8_prefix.len() + sec1_wrap.len() + 4);
|
||||
pkcs8.extend_from_slice(pkcs8_prefix);
|
||||
pkcs8.extend_from_slice(&sec1_wrap);
|
||||
wrap_in_sequence(&mut pkcs8);
|
||||
|
||||
EcdsaKeyPair::from_pkcs8(sigalg, &pkcs8).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
// This is (line-by-line):
|
||||
// - INTEGER Version = 0
|
||||
// - SEQUENCE (privateKeyAlgorithm)
|
||||
// - id-ecPublicKey OID
|
||||
// - prime256v1 OID
|
||||
const PKCS8_PREFIX_ECDSA_NISTP256: &[u8] = b"\x02\x01\x00\
|
||||
\x30\x13\
|
||||
\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\
|
||||
\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07";
|
||||
|
||||
// This is (line-by-line):
|
||||
// - INTEGER Version = 0
|
||||
// - SEQUENCE (privateKeyAlgorithm)
|
||||
// - id-ecPublicKey OID
|
||||
// - secp384r1 OID
|
||||
const PKCS8_PREFIX_ECDSA_NISTP384: &[u8] = b"\x02\x01\x00\
|
||||
\x30\x10\
|
||||
\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\
|
||||
\x06\x05\x2b\x81\x04\x00\x22";
|
||||
|
||||
impl SigningKey for EcdsaSigningKey {
|
||||
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
|
||||
if offered.contains(&self.scheme) {
|
||||
Some(Box::new(EcdsaSigner {
|
||||
key: Arc::clone(&self.key),
|
||||
scheme: self.scheme,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn algorithm(&self) -> SignatureAlgorithm {
|
||||
use crate::msgs::handshake::DecomposedSignatureScheme;
|
||||
self.scheme.sign()
|
||||
}
|
||||
}
|
||||
|
||||
struct EcdsaSigner {
|
||||
key: Arc<EcdsaKeyPair>,
|
||||
scheme: SignatureScheme,
|
||||
}
|
||||
|
||||
impl Signer for EcdsaSigner {
|
||||
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
self.key
|
||||
.sign(&rng, message)
|
||||
.map_err(|_| Error::General("signing failed".into()))
|
||||
.map(|sig| sig.as_ref().into())
|
||||
}
|
||||
|
||||
fn scheme(&self) -> SignatureScheme {
|
||||
self.scheme
|
||||
}
|
||||
}
|
||||
|
||||
/// A SigningKey that uses exactly one TLS-level SignatureScheme
|
||||
/// and one ring-level signature::SigningAlgorithm.
|
||||
///
|
||||
/// Compare this to RsaSigningKey, which for a particular key is
|
||||
/// willing to sign with several algorithms. This is quite poor
|
||||
/// cryptography practice, but is necessary because a given RSA key
|
||||
/// is expected to work in TLS1.2 (PKCS#1 signatures) and TLS1.3
|
||||
/// (PSS signatures) -- nobody is willing to obtain certificates for
|
||||
/// different protocol versions.
|
||||
///
|
||||
/// Currently this is only implemented for Ed25519 keys.
|
||||
struct Ed25519SigningKey {
|
||||
key: Arc<Ed25519KeyPair>,
|
||||
scheme: SignatureScheme,
|
||||
}
|
||||
|
||||
impl Ed25519SigningKey {
|
||||
/// Make a new `Ed25519SigningKey` from a DER encoding in PKCS#8 format,
|
||||
/// expecting a key usable with precisely the given signature scheme.
|
||||
fn new(der: &key::PrivateKey, scheme: SignatureScheme) -> Result<Self, SignError> {
|
||||
Ed25519KeyPair::from_pkcs8_maybe_unchecked(&der.0)
|
||||
.map(|kp| Self {
|
||||
key: Arc::new(kp),
|
||||
scheme,
|
||||
})
|
||||
.map_err(|_| SignError(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl SigningKey for Ed25519SigningKey {
|
||||
fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> {
|
||||
if offered.contains(&self.scheme) {
|
||||
Some(Box::new(Ed25519Signer {
|
||||
key: Arc::clone(&self.key),
|
||||
scheme: self.scheme,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn algorithm(&self) -> SignatureAlgorithm {
|
||||
use crate::msgs::handshake::DecomposedSignatureScheme;
|
||||
self.scheme.sign()
|
||||
}
|
||||
}
|
||||
|
||||
struct Ed25519Signer {
|
||||
key: Arc<Ed25519KeyPair>,
|
||||
scheme: SignatureScheme,
|
||||
}
|
||||
|
||||
impl Signer for Ed25519Signer {
|
||||
fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
Ok(self.key.sign(message).as_ref().into())
|
||||
}
|
||||
|
||||
fn scheme(&self) -> SignatureScheme {
|
||||
self.scheme
|
||||
}
|
||||
}
|
||||
|
||||
/// The set of schemes we support for signatures and
|
||||
/// that are allowed for TLS1.3.
|
||||
pub fn supported_sign_tls13() -> &'static [SignatureScheme] {
|
||||
&[
|
||||
SignatureScheme::ECDSA_NISTP384_SHA384,
|
||||
SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||
SignatureScheme::RSA_PSS_SHA512,
|
||||
SignatureScheme::RSA_PSS_SHA384,
|
||||
SignatureScheme::RSA_PSS_SHA256,
|
||||
SignatureScheme::ED25519,
|
||||
]
|
||||
}
|
||||
|
||||
/// Errors while signing
|
||||
#[derive(Debug)]
|
||||
pub struct SignError(());
|
||||
|
||||
impl fmt::Display for SignError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("sign error")
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for SignError {}
|
||||
|
||||
#[test]
|
||||
fn can_load_ecdsa_nistp256_pkcs8() {
|
||||
let key = key::PrivateKey(include_bytes!("testdata/nistp256key.pkcs8.der").to_vec());
|
||||
assert!(any_supported_type(&key).is_ok());
|
||||
assert!(any_ecdsa_type(&key).is_ok());
|
||||
assert!(any_eddsa_type(&key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_load_ecdsa_nistp256_sec1() {
|
||||
let key = key::PrivateKey(include_bytes!("testdata/nistp256key.der").to_vec());
|
||||
assert!(any_supported_type(&key).is_ok());
|
||||
assert!(any_ecdsa_type(&key).is_ok());
|
||||
assert!(any_eddsa_type(&key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_load_ecdsa_nistp384_pkcs8() {
|
||||
let key = key::PrivateKey(include_bytes!("testdata/nistp384key.pkcs8.der").to_vec());
|
||||
assert!(any_supported_type(&key).is_ok());
|
||||
assert!(any_ecdsa_type(&key).is_ok());
|
||||
assert!(any_eddsa_type(&key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_load_ecdsa_nistp384_sec1() {
|
||||
let key = key::PrivateKey(include_bytes!("testdata/nistp384key.der").to_vec());
|
||||
assert!(any_supported_type(&key).is_ok());
|
||||
assert!(any_ecdsa_type(&key).is_ok());
|
||||
assert!(any_eddsa_type(&key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_load_eddsa_pkcs8() {
|
||||
let key = key::PrivateKey(include_bytes!("testdata/eddsakey.der").to_vec());
|
||||
assert!(any_supported_type(&key).is_ok());
|
||||
assert!(any_eddsa_type(&key).is_ok());
|
||||
assert!(any_ecdsa_type(&key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_load_rsa2048_pkcs8() {
|
||||
let key = key::PrivateKey(include_bytes!("testdata/rsa2048key.pkcs8.der").to_vec());
|
||||
assert!(any_supported_type(&key).is_ok());
|
||||
assert!(any_eddsa_type(&key).is_err());
|
||||
assert!(any_ecdsa_type(&key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_load_rsa2048_pkcs1() {
|
||||
let key = key::PrivateKey(include_bytes!("testdata/rsa2048key.pkcs1.der").to_vec());
|
||||
assert!(any_supported_type(&key).is_ok());
|
||||
assert!(any_eddsa_type(&key).is_err());
|
||||
assert!(any_ecdsa_type(&key).is_err());
|
||||
}
|
||||
254
third_party/rustls-fork-shadow-tls/src/stream.rs
vendored
Normal file
254
third_party/rustls-fork-shadow-tls/src/stream.rs
vendored
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
use crate::conn::{ConnectionCommon, SideData};
|
||||
|
||||
use std::io::{IoSlice, Read, Result, Write};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// This type implements `io::Read` and `io::Write`, encapsulating
|
||||
/// a Connection `C` and an underlying transport `T`, such as a socket.
|
||||
///
|
||||
/// This allows you to use a rustls Connection like a normal stream.
|
||||
#[derive(Debug)]
|
||||
pub struct Stream<'a, C: 'a + ?Sized, T: 'a + Read + Write + ?Sized> {
|
||||
/// Our TLS connection
|
||||
pub conn: &'a mut C,
|
||||
|
||||
/// The underlying transport, like a socket
|
||||
pub sock: &'a mut T,
|
||||
}
|
||||
|
||||
impl<'a, C, T, S> Stream<'a, C, T>
|
||||
where
|
||||
C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>,
|
||||
T: 'a + Read + Write,
|
||||
S: SideData,
|
||||
{
|
||||
/// Make a new Stream using the Connection `conn` and socket-like object
|
||||
/// `sock`. This does not fail and does no IO.
|
||||
pub fn new(conn: &'a mut C, sock: &'a mut T) -> Self {
|
||||
Self { conn, sock }
|
||||
}
|
||||
|
||||
/// If we're handshaking, complete all the IO for that.
|
||||
/// If we have data to write, write it all.
|
||||
fn complete_prior_io(&mut self) -> Result<()> {
|
||||
if self.conn.is_handshaking() {
|
||||
self.conn.complete_io(self.sock)?;
|
||||
}
|
||||
|
||||
if self.conn.wants_write() {
|
||||
self.conn.complete_io(self.sock)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C, T, S> Read for Stream<'a, C, T>
|
||||
where
|
||||
C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>,
|
||||
T: 'a + Read + Write,
|
||||
S: SideData,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.complete_prior_io()?;
|
||||
|
||||
// We call complete_io() in a loop since a single call may read only
|
||||
// a partial packet from the underlying transport. A full packet is
|
||||
// needed to get more plaintext, which we must do if EOF has not been
|
||||
// hit. Otherwise, we will prematurely signal EOF by returning 0. We
|
||||
// determine if EOF has actually been hit by checking if 0 bytes were
|
||||
// read from the underlying transport.
|
||||
while self.conn.wants_read() {
|
||||
let at_eof = self.conn.complete_io(self.sock)?.0 == 0;
|
||||
if at_eof {
|
||||
if let Ok(io_state) = self.conn.process_new_packets() {
|
||||
if at_eof && io_state.plaintext_bytes_to_read() == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.conn.reader().read(buf)
|
||||
}
|
||||
|
||||
#[cfg(read_buf)]
|
||||
fn read_buf(&mut self, cursor: std::io::BorrowedCursor<'_>) -> Result<()> {
|
||||
self.complete_prior_io()?;
|
||||
|
||||
// We call complete_io() in a loop since a single call may read only
|
||||
// a partial packet from the underlying transport. A full packet is
|
||||
// needed to get more plaintext, which we must do if EOF has not been
|
||||
// hit. Otherwise, we will prematurely signal EOF by returning without
|
||||
// writing anything. We determine if EOF has actually been hit by
|
||||
// checking if 0 bytes were read from the underlying transport.
|
||||
while self.conn.wants_read() {
|
||||
let at_eof = self.conn.complete_io(self.sock)?.0 == 0;
|
||||
if at_eof {
|
||||
if let Ok(io_state) = self.conn.process_new_packets() {
|
||||
if at_eof && io_state.plaintext_bytes_to_read() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.conn.reader().read_buf(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C, T, S> Write for Stream<'a, C, T>
|
||||
where
|
||||
C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>,
|
||||
T: 'a + Read + Write,
|
||||
S: SideData,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.complete_prior_io()?;
|
||||
|
||||
let len = self.conn.writer().write(buf)?;
|
||||
|
||||
// Try to write the underlying transport here, but don't let
|
||||
// any errors mask the fact we've consumed `len` bytes.
|
||||
// Callers will learn of permanent errors on the next call.
|
||||
let _ = self.conn.complete_io(self.sock);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
|
||||
self.complete_prior_io()?;
|
||||
|
||||
let len = self
|
||||
.conn
|
||||
.writer()
|
||||
.write_vectored(bufs)?;
|
||||
|
||||
// Try to write the underlying transport here, but don't let
|
||||
// any errors mask the fact we've consumed `len` bytes.
|
||||
// Callers will learn of permanent errors on the next call.
|
||||
let _ = self.conn.complete_io(self.sock);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.complete_prior_io()?;
|
||||
|
||||
self.conn.writer().flush()?;
|
||||
if self.conn.wants_write() {
|
||||
self.conn.complete_io(self.sock)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This type implements `io::Read` and `io::Write`, encapsulating
|
||||
/// and owning a Connection `C` and an underlying blocking transport
|
||||
/// `T`, such as a socket.
|
||||
///
|
||||
/// This allows you to use a rustls Connection like a normal stream.
|
||||
#[derive(Debug)]
|
||||
pub struct StreamOwned<C: Sized, T: Read + Write + Sized> {
|
||||
/// Our connection
|
||||
pub conn: C,
|
||||
|
||||
/// The underlying transport, like a socket
|
||||
pub sock: T,
|
||||
}
|
||||
|
||||
impl<C, T, S> StreamOwned<C, T>
|
||||
where
|
||||
C: DerefMut + Deref<Target = ConnectionCommon<S>>,
|
||||
T: Read + Write,
|
||||
S: SideData,
|
||||
{
|
||||
/// Make a new StreamOwned taking the Connection `conn` and socket-like
|
||||
/// object `sock`. This does not fail and does no IO.
|
||||
///
|
||||
/// This is the same as `Stream::new` except `conn` and `sock` are
|
||||
/// moved into the StreamOwned.
|
||||
pub fn new(conn: C, sock: T) -> Self {
|
||||
Self { conn, sock }
|
||||
}
|
||||
|
||||
/// Get a reference to the underlying socket
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.sock
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the underlying socket
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.sock
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C, T, S> StreamOwned<C, T>
|
||||
where
|
||||
C: DerefMut + Deref<Target = ConnectionCommon<S>>,
|
||||
T: Read + Write,
|
||||
S: SideData,
|
||||
{
|
||||
fn as_stream(&'a mut self) -> Stream<'a, C, T> {
|
||||
Stream {
|
||||
conn: &mut self.conn,
|
||||
sock: &mut self.sock,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T, S> Read for StreamOwned<C, T>
|
||||
where
|
||||
C: DerefMut + Deref<Target = ConnectionCommon<S>>,
|
||||
T: Read + Write,
|
||||
S: SideData,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.as_stream().read(buf)
|
||||
}
|
||||
|
||||
#[cfg(read_buf)]
|
||||
fn read_buf(&mut self, cursor: std::io::BorrowedCursor<'_>) -> Result<()> {
|
||||
self.as_stream().read_buf(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, T, S> Write for StreamOwned<C, T>
|
||||
where
|
||||
C: DerefMut + Deref<Target = ConnectionCommon<S>>,
|
||||
T: Read + Write,
|
||||
S: SideData,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.as_stream().write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.as_stream().flush()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Stream, StreamOwned};
|
||||
use crate::client::ClientConnection;
|
||||
use crate::server::ServerConnection;
|
||||
use std::net::TcpStream;
|
||||
|
||||
#[test]
|
||||
fn stream_can_be_created_for_connection_and_tcpstream() {
|
||||
type _Test<'a> = Stream<'a, ClientConnection, TcpStream>;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn streamowned_can_be_created_for_client_and_tcpstream() {
|
||||
type _Test = StreamOwned<ClientConnection, TcpStream>;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn streamowned_can_be_created_for_server_and_tcpstream() {
|
||||
type _Test = StreamOwned<ServerConnection, TcpStream>;
|
||||
}
|
||||
}
|
||||
340
third_party/rustls-fork-shadow-tls/src/suites.rs
vendored
Normal file
340
third_party/rustls-fork-shadow-tls/src/suites.rs
vendored
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
use std::fmt;
|
||||
|
||||
use crate::enums::{CipherSuite, ProtocolVersion, SignatureScheme};
|
||||
use crate::msgs::enums::SignatureAlgorithm;
|
||||
use crate::msgs::handshake::DecomposedSignatureScheme;
|
||||
#[cfg(feature = "tls12")]
|
||||
use crate::tls12::Tls12CipherSuite;
|
||||
#[cfg(feature = "tls12")]
|
||||
use crate::tls12::{
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
// TLS1.2 suites
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
};
|
||||
use crate::tls13::Tls13CipherSuite;
|
||||
use crate::tls13::{
|
||||
TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256,
|
||||
};
|
||||
#[cfg(feature = "tls12")]
|
||||
use crate::versions::TLS12;
|
||||
use crate::versions::{SupportedProtocolVersion, TLS13};
|
||||
|
||||
/// Bulk symmetric encryption scheme used by a cipher suite.
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum BulkAlgorithm {
|
||||
/// AES with 128-bit keys in Galois counter mode.
|
||||
Aes128Gcm,
|
||||
|
||||
/// AES with 256-bit keys in Galois counter mode.
|
||||
Aes256Gcm,
|
||||
|
||||
/// Chacha20 for confidentiality with poly1305 for authenticity.
|
||||
Chacha20Poly1305,
|
||||
}
|
||||
|
||||
/// Common state for cipher suites (both for TLS 1.2 and TLS 1.3)
|
||||
#[derive(Debug)]
|
||||
pub struct CipherSuiteCommon {
|
||||
/// The TLS enumeration naming this cipher suite.
|
||||
pub suite: CipherSuite,
|
||||
|
||||
/// How to do bulk encryption.
|
||||
pub bulk: BulkAlgorithm,
|
||||
|
||||
pub(crate) aead_algorithm: &'static ring::aead::Algorithm,
|
||||
}
|
||||
|
||||
/// A cipher suite supported by rustls.
|
||||
///
|
||||
/// All possible instances of this type are provided by the library in
|
||||
/// the [`ALL_CIPHER_SUITES`] array.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum SupportedCipherSuite {
|
||||
/// A TLS 1.2 cipher suite
|
||||
#[cfg(feature = "tls12")]
|
||||
Tls12(&'static Tls12CipherSuite),
|
||||
/// A TLS 1.3 cipher suite
|
||||
Tls13(&'static Tls13CipherSuite),
|
||||
}
|
||||
|
||||
impl fmt::Debug for SupportedCipherSuite {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.suite().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl SupportedCipherSuite {
|
||||
/// Which hash function to use with this suite.
|
||||
pub fn hash_algorithm(&self) -> &'static ring::digest::Algorithm {
|
||||
match self {
|
||||
#[cfg(feature = "tls12")]
|
||||
Self::Tls12(inner) => inner.hash_algorithm(),
|
||||
Self::Tls13(inner) => inner.hash_algorithm(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The cipher suite's identifier
|
||||
pub fn suite(&self) -> CipherSuite {
|
||||
self.common().suite
|
||||
}
|
||||
|
||||
pub(crate) fn common(&self) -> &CipherSuiteCommon {
|
||||
match self {
|
||||
#[cfg(feature = "tls12")]
|
||||
Self::Tls12(inner) => &inner.common,
|
||||
Self::Tls13(inner) => &inner.common,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn tls13(&self) -> Option<&'static Tls13CipherSuite> {
|
||||
match self {
|
||||
#[cfg(feature = "tls12")]
|
||||
Self::Tls12(_) => None,
|
||||
Self::Tls13(inner) => Some(inner),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return supported protocol version for the cipher suite.
|
||||
pub fn version(&self) -> &'static SupportedProtocolVersion {
|
||||
match self {
|
||||
#[cfg(feature = "tls12")]
|
||||
Self::Tls12(_) => &TLS12,
|
||||
Self::Tls13(_) => &TLS13,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if this suite is usable for a key only offering `sig_alg`
|
||||
/// signatures. This resolves to true for all TLS1.3 suites.
|
||||
pub fn usable_for_signature_algorithm(&self, _sig_alg: SignatureAlgorithm) -> bool {
|
||||
match self {
|
||||
Self::Tls13(_) => true, // no constraint expressed by ciphersuite (e.g., TLS1.3)
|
||||
#[cfg(feature = "tls12")]
|
||||
Self::Tls12(inner) => inner
|
||||
.sign
|
||||
.iter()
|
||||
.any(|scheme| scheme.sign() == _sig_alg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of all the cipher suites supported by rustls.
|
||||
pub static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = &[
|
||||
// TLS1.3 suites
|
||||
TLS13_AES_256_GCM_SHA384,
|
||||
TLS13_AES_128_GCM_SHA256,
|
||||
TLS13_CHACHA20_POLY1305_SHA256,
|
||||
// TLS1.2 suites
|
||||
#[cfg(feature = "tls12")]
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
#[cfg(feature = "tls12")]
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
#[cfg(feature = "tls12")]
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
#[cfg(feature = "tls12")]
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
#[cfg(feature = "tls12")]
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
#[cfg(feature = "tls12")]
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
];
|
||||
|
||||
/// The cipher suite configuration that an application should use by default.
|
||||
///
|
||||
/// This will be [`ALL_CIPHER_SUITES`] sans any supported cipher suites that
|
||||
/// shouldn't be enabled by most applications.
|
||||
pub static DEFAULT_CIPHER_SUITES: &[SupportedCipherSuite] = ALL_CIPHER_SUITES;
|
||||
|
||||
// These both O(N^2)!
|
||||
pub(crate) fn choose_ciphersuite_preferring_client(
|
||||
client_suites: &[CipherSuite],
|
||||
server_suites: &[SupportedCipherSuite],
|
||||
) -> Option<SupportedCipherSuite> {
|
||||
for client_suite in client_suites {
|
||||
if let Some(selected) = server_suites
|
||||
.iter()
|
||||
.find(|x| *client_suite == x.suite())
|
||||
{
|
||||
return Some(*selected);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn choose_ciphersuite_preferring_server(
|
||||
client_suites: &[CipherSuite],
|
||||
server_suites: &[SupportedCipherSuite],
|
||||
) -> Option<SupportedCipherSuite> {
|
||||
if let Some(selected) = server_suites
|
||||
.iter()
|
||||
.find(|x| client_suites.contains(&x.suite()))
|
||||
{
|
||||
return Some(*selected);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Return a list of the ciphersuites in `all` with the suites
|
||||
/// incompatible with `SignatureAlgorithm` `sigalg` removed.
|
||||
pub(crate) fn reduce_given_sigalg(
|
||||
all: &[SupportedCipherSuite],
|
||||
sigalg: SignatureAlgorithm,
|
||||
) -> Vec<SupportedCipherSuite> {
|
||||
all.iter()
|
||||
.filter(|&&suite| suite.usable_for_signature_algorithm(sigalg))
|
||||
.copied()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Return a list of the ciphersuites in `all` with the suites
|
||||
/// incompatible with the chosen `version` removed.
|
||||
pub(crate) fn reduce_given_version(
|
||||
all: &[SupportedCipherSuite],
|
||||
version: ProtocolVersion,
|
||||
) -> Vec<SupportedCipherSuite> {
|
||||
all.iter()
|
||||
.filter(|&&suite| suite.version().version == version)
|
||||
.copied()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Return true if `sigscheme` is usable by any of the given suites.
|
||||
pub(crate) fn compatible_sigscheme_for_suites(
|
||||
sigscheme: SignatureScheme,
|
||||
common_suites: &[SupportedCipherSuite],
|
||||
) -> bool {
|
||||
let sigalg = sigscheme.sign();
|
||||
common_suites
|
||||
.iter()
|
||||
.any(|&suite| suite.usable_for_signature_algorithm(sigalg))
|
||||
}
|
||||
|
||||
/// Secrets for transmitting/receiving data over a TLS session.
|
||||
///
|
||||
/// After performing a handshake with rustls, these secrets can be extracted
|
||||
/// to configure kTLS for a socket, and have the kernel take over encryption
|
||||
/// and/or decryption.
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub struct ExtractedSecrets {
|
||||
/// sequence number and secrets for the "tx" (transmit) direction
|
||||
pub tx: (u64, ConnectionTrafficSecrets),
|
||||
|
||||
/// sequence number and secrets for the "rx" (receive) direction
|
||||
pub rx: (u64, ConnectionTrafficSecrets),
|
||||
}
|
||||
|
||||
/// [ExtractedSecrets] minus the sequence numbers
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
pub(crate) struct PartiallyExtractedSecrets {
|
||||
/// secrets for the "tx" (transmit) direction
|
||||
pub(crate) tx: ConnectionTrafficSecrets,
|
||||
|
||||
/// secrets for the "rx" (receive) direction
|
||||
pub(crate) rx: ConnectionTrafficSecrets,
|
||||
}
|
||||
|
||||
/// Secrets used to encrypt/decrypt data in a TLS session.
|
||||
///
|
||||
/// These can be used to configure kTLS for a socket in one direction.
|
||||
/// The only other piece of information needed is the sequence number,
|
||||
/// which is in [ExtractedSecrets].
|
||||
#[cfg(feature = "secret_extraction")]
|
||||
#[non_exhaustive]
|
||||
pub enum ConnectionTrafficSecrets {
|
||||
/// Secrets for the AES_128_GCM AEAD algorithm
|
||||
Aes128Gcm {
|
||||
/// key (16 bytes)
|
||||
key: [u8; 16],
|
||||
/// salt (4 bytes)
|
||||
salt: [u8; 4],
|
||||
/// initialization vector (8 bytes, chopped from key block)
|
||||
iv: [u8; 8],
|
||||
},
|
||||
|
||||
/// Secrets for the AES_256_GCM AEAD algorithm
|
||||
Aes256Gcm {
|
||||
/// key (32 bytes)
|
||||
key: [u8; 32],
|
||||
/// salt (4 bytes)
|
||||
salt: [u8; 4],
|
||||
/// initialization vector (8 bytes, chopped from key block)
|
||||
iv: [u8; 8],
|
||||
},
|
||||
|
||||
/// Secrets for the CHACHA20_POLY1305 AEAD algorithm
|
||||
Chacha20Poly1305 {
|
||||
/// key (32 bytes)
|
||||
key: [u8; 32],
|
||||
/// initialization vector (12 bytes)
|
||||
iv: [u8; 12],
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::enums::CipherSuite;
|
||||
|
||||
#[test]
|
||||
fn test_client_pref() {
|
||||
let client = vec![
|
||||
CipherSuite::TLS13_AES_128_GCM_SHA256,
|
||||
CipherSuite::TLS13_AES_256_GCM_SHA384,
|
||||
];
|
||||
let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
|
||||
let chosen = choose_ciphersuite_preferring_client(&client, &server);
|
||||
assert!(chosen.is_some());
|
||||
assert_eq!(chosen.unwrap(), TLS13_AES_128_GCM_SHA256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_server_pref() {
|
||||
let client = vec![
|
||||
CipherSuite::TLS13_AES_128_GCM_SHA256,
|
||||
CipherSuite::TLS13_AES_256_GCM_SHA384,
|
||||
];
|
||||
let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
|
||||
let chosen = choose_ciphersuite_preferring_server(&client, &server);
|
||||
assert!(chosen.is_some());
|
||||
assert_eq!(chosen.unwrap(), TLS13_AES_256_GCM_SHA384);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pref_fails() {
|
||||
assert!(choose_ciphersuite_preferring_client(
|
||||
&[CipherSuite::TLS_NULL_WITH_NULL_NULL],
|
||||
ALL_CIPHER_SUITES
|
||||
)
|
||||
.is_none());
|
||||
assert!(choose_ciphersuite_preferring_server(
|
||||
&[CipherSuite::TLS_NULL_WITH_NULL_NULL],
|
||||
ALL_CIPHER_SUITES
|
||||
)
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scs_is_debug() {
|
||||
println!("{:?}", ALL_CIPHER_SUITES);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_can_resume_to() {
|
||||
assert!(TLS13_AES_128_GCM_SHA256
|
||||
.tls13()
|
||||
.unwrap()
|
||||
.can_resume_from(crate::tls13::TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
|
||||
.is_some());
|
||||
assert!(TLS13_AES_256_GCM_SHA384
|
||||
.tls13()
|
||||
.unwrap()
|
||||
.can_resume_from(crate::tls13::TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-arstechnica.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-arstechnica.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-arstechnica.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-arstechnica.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-arstechnica.2.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-arstechnica.2.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-arstechnica.3.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-arstechnica.3.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-duckduckgo.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-duckduckgo.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-duckduckgo.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-duckduckgo.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-github.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-github.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-github.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-github.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-google.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-google.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-google.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-google.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-google.2.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-google.2.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-hn.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-hn.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-hn.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-hn.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-reddit.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-reddit.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-reddit.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-reddit.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-rustlang.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-rustlang.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-rustlang.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-rustlang.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-rustlang.2.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-rustlang.2.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-rustlang.3.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-rustlang.3.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-servo.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-servo.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-servo.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-servo.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-stackoverflow.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-stackoverflow.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-stackoverflow.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-stackoverflow.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-stackoverflow.2.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-stackoverflow.2.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-twitter.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-twitter.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-twitter.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-twitter.1.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-wapo.0.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-wapo.0.der
vendored
Normal file
Binary file not shown.
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-wapo.1.der
vendored
Normal file
BIN
third_party/rustls-fork-shadow-tls/src/testdata/cert-wapo.1.der
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue