From a5b1db24d731d10098b46f250d3d694d45b35c78 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon, 31 Jul 2023 18:11:39 -0400 Subject: [PATCH] Squashed commit of the following: commit eafb8a24963bdc7577bf4dddfc040e7876a48533 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 18:05:50 2023 -0400 shell -> bash commit 12071b8c27d9a23d72d25d9d8dfb12b95784bcc5 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 17:59:09 2023 -0400 add ubuntu quickstart back to readme commit 10bf5578012c11adb6b82d1032558ba4b7bc7296 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 17:52:06 2023 -0400 fixes commit 74adf8db7f49e85eefda00a0ebcf1ad698b81948 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 16:46:19 2023 -0400 fixes commit 0548d07f82392d74d80c1a62b3a17a136383c6fa Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 16:43:08 2023 -0400 consolidate commit cbe8f2d4b1aa702cc7c9c287e4d180d03fb0fabd Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 14:55:30 2023 -0400 remove old doc sections commit f1943214c343ecd7f418a8dde4b744a479aef5d2 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 12:25:28 2023 -0400 more content commit 882eb1dc82ba3d134b26bf6c949084b085b3f6b1 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 09:08:24 2023 -0400 fixes commit ce37d0ef3118ad88d0f6c501d62efc45de4ec8d6 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon Jul 31 09:03:45 2023 -0400 fixes commit 011d15fefc1e66c81ae57cccf01e0eb141fcafa4 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Sat Jul 29 22:59:51 2023 -0400 cmake consuming commit 7feadc1cb43bb63b9429c3d2eb256cd49572a651 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Sat Jul 29 22:27:02 2023 -0400 fixes commit 2914950c4407b90b7f253d0ceb01e34d4a06d977 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Sat Jul 29 21:34:24 2023 -0400 traditional make commit 02f984187498d2f065e63e0b612ee8d705b72f93 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Sat Jul 29 19:45:43 2023 -0400 s2n-tls build section commit 86c49837654178d9ff977b09d67814ce04b3da5e Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Sat Jul 29 11:56:32 2023 -0400 Update build documentation commit ea6d02a3a3f8d57d056361afc1b5a1752e81ed46 Author: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Fri Jul 28 16:49:21 2023 -0400 bindings: release 0.0.35 (#4122) commit 35d08ba36f754f90d883561e472d420894d37a8b Author: Justin Zhang <76919968+tinzh@users.noreply.github.com> Date: Fri Jul 28 12:31:21 2023 -0700 refactor(bench): separate out client and server connections in benching harness (#4113) Enables more better control of connections for benching experiments commit 65e74ca7c116ca40f81732c0906f6419e8c7bfa5 Author: Lindsay Stewart Date: Wed Jul 26 02:26:40 2023 -0700 Print error for 32bit test (#4107) commit b0b253ef35a670eaa4d2d84760684b4290b24072 Author: toidiu Date: Wed Jul 26 00:30:44 2023 -0700 ktls: set keys on socket and enable ktls (#4071) commit 403d5e6b293e15438f1de4c117abd9929f0cdc7c Author: Lindsay Stewart Date: Tue Jul 25 16:03:09 2023 -0700 Trying to use an invalid ticket should not mutate state (#4110) commit bce2b1a6077c5e9500ed6a2d9a40103842277273 Author: James Mayclin Date: Tue Jul 25 14:44:33 2023 -0700 fix: get_session behavior for TLS 1.3 (#4104) commit 6881358a394cc8620d3bb05698948e65e5612e68 Author: Justin Zhang <76919968+tinzh@users.noreply.github.com> Date: Tue Jul 25 10:10:21 2023 -0700 feat(bench): add different certificate signature algorithms to benchmarks (#4080) commit aab13d53abc1d5fa39d3d9f37e29d89c3991d95b Author: Justin Zhang <76919968+tinzh@users.noreply.github.com> Date: Mon Jul 24 18:17:30 2023 -0700 feat(bench): add memory bench with valgrind/massif (#4081) commit 20b01742ddd471a6449819fd7eea6f7f62820b11 Author: Justin Zhang <76919968+tinzh@users.noreply.github.com> Date: Mon Jul 24 13:26:32 2023 -0700 feat(bench): add historical performance benchmark (#4083) commit 5cc827d228db8579b7ede3556332994778b19f82 Author: Doug Chapman <54039637+dougch@users.noreply.github.com> Date: Thu Jul 20 11:50:50 2023 -0700 nix: pin corretto version (#4103) --- README.md | 55 +-- api/s2n.h | 3 + bindings/rust/bench/.gitignore | 3 + bindings/rust/bench/Cargo.toml | 14 +- bindings/rust/bench/README.md | 46 +- bindings/rust/bench/aws-lc-config/rustls.toml | 2 + bindings/rust/bench/aws-lc-config/s2n.toml | 3 + bindings/rust/bench/benches/handshake.rs | 107 +++-- bindings/rust/bench/benches/throughput.rs | 82 ++-- bindings/rust/bench/certs/ca-cert.pem | 22 - bindings/rust/bench/certs/client-cert.pem | 22 - .../rust/bench/certs/client-fullchain.pem | 44 -- bindings/rust/bench/certs/client-key.pem | 6 - bindings/rust/bench/certs/generate_certs.sh | 85 +++- bindings/rust/bench/certs/server-cert.pem | 22 - .../rust/bench/certs/server-fullchain.pem | 44 -- bindings/rust/bench/certs/server-key.pem | 6 - .../rust/bench/historical-perf/bench-past.sh | 75 +++ .../images/historical-perf-handshake.svg | 424 +++++++++++++++++ .../images/historical-perf-throughput.svg | 341 ++++++++++++++ bindings/rust/bench/install-aws-lc.sh | 84 ++++ bindings/rust/bench/memory/bench-memory.sh | 22 + bindings/rust/bench/src/bin/graph_memory.rs | 118 +++++ bindings/rust/bench/src/bin/graph_perf.rs | 303 ++++++++++++ bindings/rust/bench/src/bin/memory.rs | 67 +++ bindings/rust/bench/src/harness.rs | 431 ++++++++++++++---- bindings/rust/bench/src/lib.rs | 32 +- bindings/rust/bench/src/openssl.rs | 203 +++++---- bindings/rust/bench/src/rustls.rs | 221 +++++---- bindings/rust/bench/src/s2n_tls.rs | 296 ++++++------ .../rust/s2n-tls-sys/templates/Cargo.template | 2 +- bindings/rust/s2n-tls-tokio/Cargo.toml | 4 +- bindings/rust/s2n-tls/Cargo.toml | 4 +- .../spec/buildspec_32bit_cross_compile.yml | 2 +- docs/BUILD.md | 263 +++++++++++ docs/USAGE-GUIDE.md | 243 +--------- error/s2n_errno.c | 1 + error/s2n_errno.h | 1 + nix/amazon-corretto-17.nix | 16 +- tests/features/S2N_KTLS_SUPPORTED.c | 30 ++ tests/features/S2N_KTLS_SUPPORTED.flags | 0 tests/unit/s2n_ktls_test.c | 268 +++++++++-- tests/unit/s2n_resume_test.c | 403 ++++++++++++---- .../s2n_self_talk_session_resumption_test.c | 30 +- tls/s2n_ktls.c | 163 ++++++- tls/s2n_ktls.h | 8 +- tls/s2n_ktls_parameters.h | 32 +- tls/s2n_prf.c | 39 +- tls/s2n_prf.h | 1 + tls/s2n_resume.c | 62 ++- tls/s2n_server_hello.c | 3 + utils/s2n_mem.c | 2 + utils/s2n_mem.h | 5 + 53 files changed, 3545 insertions(+), 1220 deletions(-) create mode 100644 bindings/rust/bench/.gitignore create mode 100644 bindings/rust/bench/aws-lc-config/rustls.toml create mode 100644 bindings/rust/bench/aws-lc-config/s2n.toml delete mode 100644 bindings/rust/bench/certs/ca-cert.pem delete mode 100644 bindings/rust/bench/certs/client-cert.pem delete mode 100644 bindings/rust/bench/certs/client-fullchain.pem delete mode 100644 bindings/rust/bench/certs/client-key.pem delete mode 100644 bindings/rust/bench/certs/server-cert.pem delete mode 100644 bindings/rust/bench/certs/server-fullchain.pem delete mode 100644 bindings/rust/bench/certs/server-key.pem create mode 100755 bindings/rust/bench/historical-perf/bench-past.sh create mode 100644 bindings/rust/bench/images/historical-perf-handshake.svg create mode 100644 bindings/rust/bench/images/historical-perf-throughput.svg create mode 100755 bindings/rust/bench/install-aws-lc.sh create mode 100755 bindings/rust/bench/memory/bench-memory.sh create mode 100644 bindings/rust/bench/src/bin/graph_memory.rs create mode 100644 bindings/rust/bench/src/bin/graph_perf.rs create mode 100644 bindings/rust/bench/src/bin/memory.rs create mode 100644 docs/BUILD.md create mode 100644 tests/features/S2N_KTLS_SUPPORTED.c create mode 100644 tests/features/S2N_KTLS_SUPPORTED.flags diff --git a/README.md b/README.md index 64baa0db5c3..9636bc5dc11 100644 --- a/README.md +++ b/README.md @@ -14,54 +14,29 @@ s2n-tls is a C99 implementation of the TLS/SSL protocols that is designed to be [![Join the chat at https://gitter.im/awslabs/s2n](https://badges.gitter.im/awslabs/s2n.svg)](https://gitter.im/awslabs/s2n?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Quickstart for Ubuntu -1. Fork s2n-tls on GitHub -2. Run the following commands on Ubuntu. -``` -git clone https://github.com/${YOUR_GITHUB_ACCOUNT_NAME}/s2n-tls.git -cd s2n-tls - -# Pick an "env" line from the codebuild/codebuild.config file and run it, in this case choose the openssl-1.1.1 with GCC 9 build -S2N_LIBCRYPTO=openssl-1.1.1 BUILD_S2N=true TESTS=integrationv2 GCC_VERSION=9 - -sudo codebuild/bin/s2n_install_test_dependencies.sh -codebuild/bin/s2n_codebuild.sh -``` - -## Quickstart for OSX (or other platforms) - -If you are building on OSX, or simply don't want to execute the entire build script above, you can use build tools like Ninja. -### OSX - -An example of building on OSX: +```bash +# clone s2n-tls +git clone https://github.com/aws/s2n-tls.git +cd s2n-tls -```sh -# Install required dependencies using homebrew -brew install ninja cmake coreutils openssl@1.1 +# install build dependencies +sudo apt update +sudo apt install cmake ninja-build -# Clone the s2n-tls source repository into the `s2n-tls` directory -git clone https://github.com/${YOUR_GITHUB_ACCOUNT_NAME}/s2n-tls.git -cd s2n-tls +# install a libcrypto +sudo apt install libssl-dev -# Create a build directory, and build s2n-tls with debug symbols and a specific OpenSSL version. +# build s2n-tls cmake . -Bbuild -GNinja \ - -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_PREFIX_PATH=$(dirname $(dirname $(brew list openssl@1.1|grep libcrypto.dylib))) -cmake --build ./build -j $(nproc) + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=./s2n-tls-install +ninja -C build -j $(nproc) CTEST_PARALLEL_LEVEL=$(nproc) ninja -C build test +ninja -C build install ``` -### Amazonlinux2 - -Install dependencies with `./codebuild/bin/install_al2_dependencies.sh` after cloning. - -```sh -git clone https://github.com/${YOUR_GITHUB_ACCOUNT_NAME}/s2n-tls.git -cd s2n-tls -cmake . -Bbuild -DCMAKE_EXE_LINKER_FLAGS="-lcrypto -lz" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -cmake --build ./build -j $(nproc) -CTEST_PARALLEL_LEVEL=$(nproc) make -C build test -``` +See the [s2n-tls build documentation](docs/BUILD.md) for further guidance on building s2n-tls for your platform. ## Have a Question? If you have any questions about Submitting PR's, Opening Issues, s2n-tls API usage, or something similar, we have a public chatroom available here to answer your questions: https://gitter.im/awslabs/s2n diff --git a/api/s2n.h b/api/s2n.h index cccc2b0c927..820837cc329 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -2214,6 +2214,9 @@ S2N_API extern int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ti /** * De-serializes the session state and updates the connection accordingly. * + * If this method fails, the connection should not be affected: calling s2n_negotiate + * with the connection should simply result in a full handshake. + * * @param conn A pointer to the s2n_connection object * @param session A pointer to a buffer of size `length` * @param length The size of the `session` buffer diff --git a/bindings/rust/bench/.gitignore b/bindings/rust/bench/.gitignore new file mode 100644 index 00000000000..884cf54f178 --- /dev/null +++ b/bindings/rust/bench/.gitignore @@ -0,0 +1,3 @@ +*.pem +*.svg +!historical-perf-*.svg diff --git a/bindings/rust/bench/Cargo.toml b/bindings/rust/bench/Cargo.toml index 2ee023ff571..7fb601b56e7 100644 --- a/bindings/rust/bench/Cargo.toml +++ b/bindings/rust/bench/Cargo.toml @@ -3,16 +3,26 @@ name = "bench" version = "0.1.0" edition = "2021" +[features] +historical-perf = [] + [dependencies] s2n-tls = { path = "../s2n-tls" } rustls = "0.21" rustls-pemfile = "1.0" -openssl = "0.10" +openssl = { version = "0.10", features = ["vendored"] } errno = "0.3" libc = "0.2" +crabgrind = "0.1" +rand = "0.8" +rand_distr = "0.4" +plotters = "0.3" +serde_json = "1.0" +semver = "1.0" +strum = { version = "0.25", features = ["derive"] } [dev-dependencies] -criterion = "0.3" +criterion = "0.5" [[bench]] name = "handshake" diff --git a/bindings/rust/bench/README.md b/bindings/rust/bench/README.md index 534651e16c3..59b026036a8 100644 --- a/bindings/rust/bench/README.md +++ b/bindings/rust/bench/README.md @@ -4,12 +4,37 @@ We use to Criterion.rs to benchmark s2n-tls against two commonly used TLS librar ## Setup -Setup is easy! Just have OpenSSL installed and generate Rust bindings for s2n-tls using `bindings/rust/generate.sh`. +Setup is easy! Just have OpenSSL installed, generate Rust bindings for s2n-tls using `../generate.sh`, and generate certs using `certs/generate_certs.sh`. + +Dependencies are the same as with s2n-tls. Currently, this crate has only been tested on Ubuntu (both x86 and ARM), but we expect everything to work with other Unix environments. + +To bench with AWS-LC, Amazon's custom libcrypto implementation, first run `install-aws-lc.sh` to install AWS-LC for the bench crate. To then run the benchmarks with AWS-LC, use Cargo with either the flag `--config aws-lc-config/s2n.toml` or `--config aws-lc-config/rustls.toml` (or both). You can also append these configs to `.cargo/config.toml` to let Cargo automatically detect the settings without specifying the flags each time. + +For example, to get started with benching s2n-tls with AWS-LC: + +``` +../generate.sh +certs/generate_certs.sh +./install-aws-lc.sh +cargo bench --config aws-lc-config/s2n.toml +``` ## Running benchmarks The benchmarks can be run with the `cargo bench` command. Criterion will auto-generate an HTML report in `target/criterion/`. +To run memory benchmarks, run `memory/bench-memory.sh`. A graph of memory usage will be generated in `memory/memory.svg`. + +## Historical benchmarks + +To do historical benchmarks, run `historical-perf/bench-past.sh`. This will checkout old versions of s2n-tls back to v1.3.16 in `target/` and run benchmarks on those with the `historical-perf` feature, disabling Rustls and OpenSSL benches. + +### Caveats + +The last version benched is v1.3.16, since before that, the s2n-tls Rust bindings have a different API and would thus require a different bench harness to test. + +v1.3.30-1.3.37 are not benched because of depedency issues when generating the Rust bindings. However, versions before and after are benched, so the overall trend in performance can still be seen without the data from these versions. + ## Implementation details We use Rust bindings for s2n-tls and OpenSSL. All of our benchmarks are run in Rust on a single thread for consistency. @@ -20,8 +45,25 @@ To remove external factors, we use custom IO with our benchmarks, bypassing the ### Certificate generation -All certs are stored in `certs/` and can be regenerated using `certs/generate_certs.sh`. There is one root cert that directly signs the server and client certs that are used in benchmarking. Currently, we use ECDSA with `secp384r1`. +There is one root cert that directly signs the server and client certs that are used in benchmarking. We currently bench RSA and ECDSA certs. ### Negotiation parameters The cipher suites benchmarked are `TLS_AES_128_GCM_SHA256` and `TLS_AES_256_GCM_SHA384`, and the key exchange methods benchmarked are ECDHE with `secp256r1` and with `x25519`. We also test connections with and without client authentication (mTLS). + +## Sample output + +### Historical performance + +Because these benches take a longer time to generate (>30 min), we include the results from historical benching (as of v1.3.47) here. + +Notes: +- Two sets of parameters for the handshake couldn't be benched before 1.3.40, since security policies that negotiated those policies as their top choice did not exist before then. +- There is no data from 1.3.30 to 1.3.37 because those versions have a dependency issue that cause the Rust bindings not to build. However, there is data before and after that period, so the performance for those versions can be inferred via interpolation. +- The improvement in throughput in 1.3.28 was most likely caused by the addition of LTO to the default Rust bindings build. +- Since the benches are run over a long time, noise on the machine can cause variability, as seen in the throughput graph. +- The variability can be seen with throughput especially because it is calculated as the inverse of time taken. + +![historical-perf-handshake](images/historical-perf-handshake.svg) + +![historical-perf-throughput](images/historical-perf-throughput.svg) diff --git a/bindings/rust/bench/aws-lc-config/rustls.toml b/bindings/rust/bench/aws-lc-config/rustls.toml new file mode 100644 index 00000000000..c1a5fe31c4b --- /dev/null +++ b/bindings/rust/bench/aws-lc-config/rustls.toml @@ -0,0 +1,2 @@ +[patch.crates-io] +rustls = { path = "target/rustls/rustls" } diff --git a/bindings/rust/bench/aws-lc-config/s2n.toml b/bindings/rust/bench/aws-lc-config/s2n.toml new file mode 100644 index 00000000000..d781f39231f --- /dev/null +++ b/bindings/rust/bench/aws-lc-config/s2n.toml @@ -0,0 +1,3 @@ +[env] +S2N_TLS_LIB_DIR = "target/s2n-tls-build/lib" +LD_LIBRARY_PATH = "target/s2n-tls-build/lib" diff --git a/bindings/rust/bench/benches/handshake.rs b/bindings/rust/bench/benches/handshake.rs index 767651e5b5f..99eff7617c0 100644 --- a/bindings/rust/bench/benches/handshake.rs +++ b/bindings/rust/bench/benches/handshake.rs @@ -2,57 +2,76 @@ // SPDX-License-Identifier: Apache-2.0 use bench::{ - CryptoConfig, - ECGroup::{self, *}, - HandshakeType::{self, *}, - OpenSslHarness, RustlsHarness, S2NHarness, TlsBenchHarness, + CipherSuite, CryptoConfig, HandshakeType, KXGroup, OpenSslConnection, RustlsConnection, + S2NConnection, SigType, TlsConnPair, TlsConnection, }; use criterion::{ criterion_group, criterion_main, measurement::WallTime, BatchSize, BenchmarkGroup, Criterion, }; -use std::any::type_name; +use strum::IntoEnumIterator; + +fn bench_handshake_for_library( + bench_group: &mut BenchmarkGroup, + handshake_type: HandshakeType, + kx_group: KXGroup, + sig_type: SigType, +) { + // generate all harnesses (TlsConnPair structs) beforehand so that benchmarks + // only include negotiation and not config/connection initialization + bench_group.bench_function(T::name(), |b| { + b.iter_batched_ref( + || { + TlsConnPair::::new( + CryptoConfig::new(CipherSuite::default(), kx_group, sig_type), + handshake_type, + Default::default(), + ) + }, + |conn_pair_res| { + // harnesses with certain parameters fail to initialize for + // some past versions of s2n-tls, but missing data can be + // visually interpolated in the historical performance graph + if let Ok(conn_pair) = conn_pair_res { + let _ = conn_pair.handshake(); + } + }, + BatchSize::SmallInput, + ) + }); +} pub fn bench_handshake_params(c: &mut Criterion) { - fn bench_handshake_for_library( - bench_group: &mut BenchmarkGroup, - handshake_type: HandshakeType, - ec_group: ECGroup, - ) { - bench_group.bench_function(type_name::(), |b| { - b.iter_batched_ref( - || { - T::new( - CryptoConfig { - cipher_suite: Default::default(), - ec_group, - }, + for handshake_type in HandshakeType::iter() { + for kx_group in KXGroup::iter() { + for sig_type in SigType::iter() { + let mut bench_group = c.benchmark_group(match handshake_type { + HandshakeType::ServerAuth => format!("handshake-{:?}-{:?}", kx_group, sig_type), + HandshakeType::MutualAuth => { + format!("handshake-mTLS-{:?}-{:?}", kx_group, sig_type) + } + }); + bench_handshake_for_library::( + &mut bench_group, + handshake_type, + kx_group, + sig_type, + ); + #[cfg(not(feature = "historical-perf"))] + { + bench_handshake_for_library::( + &mut bench_group, handshake_type, - ) - .unwrap() - }, - |harness| { - harness.handshake().unwrap(); - }, - BatchSize::SmallInput, - ) - }); - } - - for handshake_type in [ServerAuth, MutualAuth] { - for ec_group in [SECP256R1, X25519] { - let mut bench_group = - c.benchmark_group(format!("handshake-{:?}-{:?}", handshake_type, ec_group)); - bench_handshake_for_library::(&mut bench_group, handshake_type, ec_group); - bench_handshake_for_library::( - &mut bench_group, - handshake_type, - ec_group, - ); - bench_handshake_for_library::( - &mut bench_group, - handshake_type, - ec_group, - ); + kx_group, + sig_type, + ); + bench_handshake_for_library::( + &mut bench_group, + handshake_type, + kx_group, + sig_type, + ); + } + } } } } diff --git a/bindings/rust/bench/benches/throughput.rs b/bindings/rust/bench/benches/throughput.rs index 183053e7836..bbb2121f586 100644 --- a/bindings/rust/bench/benches/throughput.rs +++ b/bindings/rust/bench/benches/throughput.rs @@ -2,58 +2,68 @@ // SPDX-License-Identifier: Apache-2.0 use bench::{ - CipherSuite::{self, *}, - CryptoConfig, OpenSslHarness, RustlsHarness, S2NHarness, TlsBenchHarness, + harness::ConnectedBuffer, CipherSuite, CryptoConfig, HandshakeType, KXGroup, OpenSslConnection, + RustlsConnection, S2NConnection, SigType, TlsConnPair, TlsConnection, }; use criterion::{ criterion_group, criterion_main, measurement::WallTime, BatchSize, BenchmarkGroup, Criterion, Throughput, }; -use std::any::type_name; +use strum::IntoEnumIterator; + +fn bench_throughput_for_library( + bench_group: &mut BenchmarkGroup, + shared_buf: &mut [u8], + cipher_suite: CipherSuite, +) { + bench_group.bench_function(T::name(), |b| { + b.iter_batched_ref( + || { + TlsConnPair::::new( + CryptoConfig::new(cipher_suite, KXGroup::default(), SigType::default()), + HandshakeType::default(), + ConnectedBuffer::default(), + ) + .map(|mut h| { + let _ = h.handshake(); + h + }) + }, + |conn_pair_res| { + if let Ok(conn_pair) = conn_pair_res { + let _ = conn_pair.round_trip_transfer(shared_buf); + } + }, + BatchSize::SmallInput, + ) + }); +} pub fn bench_throughput_cipher_suite(c: &mut Criterion) { // arbitrarily large to cut across TLS record boundaries let mut shared_buf = [0u8; 100000]; - fn bench_throughput_for_library( - bench_group: &mut BenchmarkGroup, - shared_buf: &mut [u8], - cipher_suite: CipherSuite, - ) { - bench_group.bench_function(type_name::(), |b| { - b.iter_batched_ref( - || { - let mut harness = T::new( - CryptoConfig { - cipher_suite, - ec_group: Default::default(), - }, - Default::default(), - ) - .unwrap(); - harness.handshake().unwrap(); - harness - }, - |harness| harness.round_trip_transfer(shared_buf).unwrap(), - BatchSize::SmallInput, - ) - }); - } - - for cipher_suite in [AES_128_GCM_SHA256, AES_256_GCM_SHA384] { + for cipher_suite in CipherSuite::iter() { let mut bench_group = c.benchmark_group(format!("throughput-{:?}", cipher_suite)); bench_group.throughput(Throughput::Bytes(shared_buf.len() as u64)); - bench_throughput_for_library::(&mut bench_group, &mut shared_buf, cipher_suite); - bench_throughput_for_library::( - &mut bench_group, - &mut shared_buf, - cipher_suite, - ); - bench_throughput_for_library::( + bench_throughput_for_library::( &mut bench_group, &mut shared_buf, cipher_suite, ); + #[cfg(not(feature = "historical-perf"))] + { + bench_throughput_for_library::( + &mut bench_group, + &mut shared_buf, + cipher_suite, + ); + bench_throughput_for_library::( + &mut bench_group, + &mut shared_buf, + cipher_suite, + ); + } } } diff --git a/bindings/rust/bench/certs/ca-cert.pem b/bindings/rust/bench/certs/ca-cert.pem deleted file mode 100644 index 636079e433b..00000000000 --- a/bindings/rust/bench/certs/ca-cert.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDnTCCAoUCFHd1xnhLaVEsJVDIz0PHH1mclbPuMA0GCSqGSIb3DQEBCwUAMIGJ -MQswCQYDVQQGEwJKUDEOMAwGA1UECAwFQ2hpYmExEzARBgNVBAcMCkNoaWJhIENp -dHkxGDAWBgNVBAoMD1Rlc3NpZXItQXNocG9vbDEYMBYGA1UEAwwPZGV2ZWxvcC5s -b2NhbGNhMSEwHwYJKoZIhvcNAQkBFhJjYUBkZXZlbG9wLmxvY2FsY2EwIBcNMjMw -NjA5MTcyNTAyWhgPMjIwMjExMTQxNzI1MDJaMIGJMQswCQYDVQQGEwJKUDEOMAwG -A1UECAwFQ2hpYmExEzARBgNVBAcMCkNoaWJhIENpdHkxGDAWBgNVBAoMD1Rlc3Np -ZXItQXNocG9vbDEYMBYGA1UEAwwPZGV2ZWxvcC5sb2NhbGNhMSEwHwYJKoZIhvcN -AQkBFhJjYUBkZXZlbG9wLmxvY2FsY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDII4MvapIuBLmDoIn/jeu9xeHqw8vUiPZecg4azweLv6B/QKKQSAXF -bQA2JVCSHF7gxjHDvOFG3+Kx/RV74hKjHlp2yVMskW1r4e6WCC+duIBgR2y7xfaM -7C5XKt6DuSexHECmUiU9cG2ZRP8lx8Wclvet9Ob+iP+sx3xQJ8Y0NtLjeo5UCV5X -CAAVBUHLTypRNSsU6LFxkRjs2JxdjGLG98+ih4VNfc5lUXBJzcrCSLErnaIhvxcz -X6myHd57eCYsWEsukuGcY+59rgPkxDofK+G/PwMOMVjYeQ1HoR/Ms9+mgN88Udcm -d9IXCKZh1dmqbBZMOXZi5fjaViO8xhe1AgMBAAEwDQYJKoZIhvcNAQELBQADggEB -AKA3FgjF97cFyT0EfZfJag3g5sSq1NLqGuAvmtgaUhdqRT2vo5Uuaj+ZqxmFvEll -tb9zeQgaPksyW+wZ+Lg46qYHhwuInycqke6Sa4FKlQrydThoF9tM1zQtnJqzdX1s -gcm731nR2O3lYsrfNRasOeB11rk4a14cJRwWk57d8qt/nLwmPMt9dc4IsCmqQw6l -s0f49lX/PmEenAaO5hfVI7XcasT260L4/NlFA2HzrbGdO+o2aCrSfA5+Qt0PMsjZ -H2P+1/JefgRyMDG8hk0ev2aoqkGBvZrSJyCI/+EZoJRuS1qPI3S+kHGXkJ3tF4i+ -IeTBeNaFh9ayc/UoUPyved0= ------END CERTIFICATE----- diff --git a/bindings/rust/bench/certs/client-cert.pem b/bindings/rust/bench/certs/client-cert.pem deleted file mode 100644 index 0975dfc4e54..00000000000 --- a/bindings/rust/bench/certs/client-cert.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIUNCurbgaG3FFx/oedAfC+QM7PJI4wDQYJKoZIhvcNAQEL -BQAwgYkxCzAJBgNVBAYTAkpQMQ4wDAYDVQQIDAVDaGliYTETMBEGA1UEBwwKQ2hp -YmEgQ2l0eTEYMBYGA1UECgwPVGVzc2llci1Bc2hwb29sMRgwFgYDVQQDDA9kZXZl -bG9wLmxvY2FsY2ExITAfBgkqhkiG9w0BCQEWEmNhQGRldmVsb3AubG9jYWxjYTAg -Fw0yMzA2MDkxNzI1MDJaGA8yMjAyMTExNDE3MjUwMlowYDELMAkGA1UEBhMCVVMx -EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDzANBgNVBAoM -BmNsaWVudDEZMBcGA1UEAwwQY2xpZW50LmxvY2FsaG9zdDB2MBAGByqGSM49AgEG -BSuBBAAiA2IABPCBEz2mGh83HkcxVHCZeKnQnl4RcJI4kznhAMSGRrFl9yEkbypf -wrOCTwytVQDn9ZLFR8BRWAlOQvaC7lYN0nVtA0dHpLJEATilvRzf3a4QUznh8Rbp -svRRUvOXoXWTM6OB7jCB6zAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYE -FAEG/BipFATRiCJUrQTLvrrsxwHMMIGzBgNVHSMEgaswgaihgY+kgYwwgYkxCzAJ -BgNVBAYTAkpQMQ4wDAYDVQQIDAVDaGliYTETMBEGA1UEBwwKQ2hpYmEgQ2l0eTEY -MBYGA1UECgwPVGVzc2llci1Bc2hwb29sMRgwFgYDVQQDDA9kZXZlbG9wLmxvY2Fs -Y2ExITAfBgkqhkiG9w0BCQEWEmNhQGRldmVsb3AubG9jYWxjYYIUd3XGeEtpUSwl -UMjPQ8cfWZyVs+4wDQYJKoZIhvcNAQELBQADggEBAKsuZKK0mBcop0ZakO5tS8pm -5BLqymrC3yhEoEKTS6Tf6ek+Fr/++3bIiFeU1+X84Pd6/cfHRZp1RHykDPiLZy5S -xNZr+wsg1Dkx3GT521c/wievUTrqZ0G5/CK5On2jC5ytFHuARF9XNqYM/WhvSHN0 -q/ZLUBqyOOGUsQ5BHatgPuEKXp3n7Qp0yW4Wy/s16f3jRi4KCbGAQC+Q64OXzBzr -rFMNGrLHJrusv4TIKQ/UR5A4JLuRChk1UdxlErowyID4UOOyzHroK73dFmwg/jGu -65miqd2SX8KcKC2Jiq+YCAnzIpuigiyz0EgFAV/pdUZB1qP3hEwb4hJItz3vyAo= ------END CERTIFICATE----- diff --git a/bindings/rust/bench/certs/client-fullchain.pem b/bindings/rust/bench/certs/client-fullchain.pem deleted file mode 100644 index f0031f9b2fb..00000000000 --- a/bindings/rust/bench/certs/client-fullchain.pem +++ /dev/null @@ -1,44 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIUNCurbgaG3FFx/oedAfC+QM7PJI4wDQYJKoZIhvcNAQEL -BQAwgYkxCzAJBgNVBAYTAkpQMQ4wDAYDVQQIDAVDaGliYTETMBEGA1UEBwwKQ2hp -YmEgQ2l0eTEYMBYGA1UECgwPVGVzc2llci1Bc2hwb29sMRgwFgYDVQQDDA9kZXZl -bG9wLmxvY2FsY2ExITAfBgkqhkiG9w0BCQEWEmNhQGRldmVsb3AubG9jYWxjYTAg -Fw0yMzA2MDkxNzI1MDJaGA8yMjAyMTExNDE3MjUwMlowYDELMAkGA1UEBhMCVVMx -EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDzANBgNVBAoM -BmNsaWVudDEZMBcGA1UEAwwQY2xpZW50LmxvY2FsaG9zdDB2MBAGByqGSM49AgEG -BSuBBAAiA2IABPCBEz2mGh83HkcxVHCZeKnQnl4RcJI4kznhAMSGRrFl9yEkbypf -wrOCTwytVQDn9ZLFR8BRWAlOQvaC7lYN0nVtA0dHpLJEATilvRzf3a4QUznh8Rbp -svRRUvOXoXWTM6OB7jCB6zAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYE -FAEG/BipFATRiCJUrQTLvrrsxwHMMIGzBgNVHSMEgaswgaihgY+kgYwwgYkxCzAJ -BgNVBAYTAkpQMQ4wDAYDVQQIDAVDaGliYTETMBEGA1UEBwwKQ2hpYmEgQ2l0eTEY -MBYGA1UECgwPVGVzc2llci1Bc2hwb29sMRgwFgYDVQQDDA9kZXZlbG9wLmxvY2Fs -Y2ExITAfBgkqhkiG9w0BCQEWEmNhQGRldmVsb3AubG9jYWxjYYIUd3XGeEtpUSwl -UMjPQ8cfWZyVs+4wDQYJKoZIhvcNAQELBQADggEBAKsuZKK0mBcop0ZakO5tS8pm -5BLqymrC3yhEoEKTS6Tf6ek+Fr/++3bIiFeU1+X84Pd6/cfHRZp1RHykDPiLZy5S -xNZr+wsg1Dkx3GT521c/wievUTrqZ0G5/CK5On2jC5ytFHuARF9XNqYM/WhvSHN0 -q/ZLUBqyOOGUsQ5BHatgPuEKXp3n7Qp0yW4Wy/s16f3jRi4KCbGAQC+Q64OXzBzr -rFMNGrLHJrusv4TIKQ/UR5A4JLuRChk1UdxlErowyID4UOOyzHroK73dFmwg/jGu -65miqd2SX8KcKC2Jiq+YCAnzIpuigiyz0EgFAV/pdUZB1qP3hEwb4hJItz3vyAo= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDnTCCAoUCFHd1xnhLaVEsJVDIz0PHH1mclbPuMA0GCSqGSIb3DQEBCwUAMIGJ -MQswCQYDVQQGEwJKUDEOMAwGA1UECAwFQ2hpYmExEzARBgNVBAcMCkNoaWJhIENp -dHkxGDAWBgNVBAoMD1Rlc3NpZXItQXNocG9vbDEYMBYGA1UEAwwPZGV2ZWxvcC5s -b2NhbGNhMSEwHwYJKoZIhvcNAQkBFhJjYUBkZXZlbG9wLmxvY2FsY2EwIBcNMjMw -NjA5MTcyNTAyWhgPMjIwMjExMTQxNzI1MDJaMIGJMQswCQYDVQQGEwJKUDEOMAwG -A1UECAwFQ2hpYmExEzARBgNVBAcMCkNoaWJhIENpdHkxGDAWBgNVBAoMD1Rlc3Np -ZXItQXNocG9vbDEYMBYGA1UEAwwPZGV2ZWxvcC5sb2NhbGNhMSEwHwYJKoZIhvcN -AQkBFhJjYUBkZXZlbG9wLmxvY2FsY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDII4MvapIuBLmDoIn/jeu9xeHqw8vUiPZecg4azweLv6B/QKKQSAXF -bQA2JVCSHF7gxjHDvOFG3+Kx/RV74hKjHlp2yVMskW1r4e6WCC+duIBgR2y7xfaM -7C5XKt6DuSexHECmUiU9cG2ZRP8lx8Wclvet9Ob+iP+sx3xQJ8Y0NtLjeo5UCV5X -CAAVBUHLTypRNSsU6LFxkRjs2JxdjGLG98+ih4VNfc5lUXBJzcrCSLErnaIhvxcz -X6myHd57eCYsWEsukuGcY+59rgPkxDofK+G/PwMOMVjYeQ1HoR/Ms9+mgN88Udcm -d9IXCKZh1dmqbBZMOXZi5fjaViO8xhe1AgMBAAEwDQYJKoZIhvcNAQELBQADggEB -AKA3FgjF97cFyT0EfZfJag3g5sSq1NLqGuAvmtgaUhdqRT2vo5Uuaj+ZqxmFvEll -tb9zeQgaPksyW+wZ+Lg46qYHhwuInycqke6Sa4FKlQrydThoF9tM1zQtnJqzdX1s -gcm731nR2O3lYsrfNRasOeB11rk4a14cJRwWk57d8qt/nLwmPMt9dc4IsCmqQw6l -s0f49lX/PmEenAaO5hfVI7XcasT260L4/NlFA2HzrbGdO+o2aCrSfA5+Qt0PMsjZ -H2P+1/JefgRyMDG8hk0ev2aoqkGBvZrSJyCI/+EZoJRuS1qPI3S+kHGXkJ3tF4i+ -IeTBeNaFh9ayc/UoUPyved0= ------END CERTIFICATE----- diff --git a/bindings/rust/bench/certs/client-key.pem b/bindings/rust/bench/certs/client-key.pem deleted file mode 100644 index 775648870fc..00000000000 --- a/bindings/rust/bench/certs/client-key.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBuxa4+TKypIlwZT2N3 -G3pMGDTJgrxTCfuc9QX917hf5ulXz915VIJkUcRFe+AXVkOhZANiAATwgRM9phof -Nx5HMVRwmXip0J5eEXCSOJM54QDEhkaxZfchJG8qX8Kzgk8MrVUA5/WSxUfAUVgJ -TkL2gu5WDdJ1bQNHR6SyRAE4pb0c392uEFM54fEW6bL0UVLzl6F1kzM= ------END PRIVATE KEY----- diff --git a/bindings/rust/bench/certs/generate_certs.sh b/bindings/rust/bench/certs/generate_certs.sh index d1cb30b75fc..36f6807c0c0 100755 --- a/bindings/rust/bench/certs/generate_certs.sh +++ b/bindings/rust/bench/certs/generate_certs.sh @@ -1,41 +1,76 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 +# Usage: ./generate_certs.sh [clean] +# Generates all necessary certs for benching +# Use argument "clean" to remove all generated certs + # immediately bail if any command fails set -e -pushd "$(dirname "$0")" +# go to directory script is located in +pushd "$(dirname "$0")" > /dev/null + +# Generates certs with given algorithms and bits in $1$2/, ex. ec384/ +# $1: rsa or ec +# $2: number of bits +# $3: directory under the `certs/` directory to put certs in +cert-gen () { + echo -e "\n----- generating certs for $1$2 -----\n" + + key_family=$1 + key_size=$2 + dir_name=$3 + + # set openssl argument name + if [[ $key_family == rsa ]]; then + local argname=rsa_keygen_bits: + elif [[ $key_family == ec ]]; then + local argname=ec_paramgen_curve:P- + fi + + # make directory for certs + mkdir -p $dir_name + cd $dir_name + + echo "generating CA private key and certificate" + openssl req -new -nodes -x509 -newkey $key_family -pkeyopt $argname$key_size -keyout ca-key.pem -out ca-cert.pem -days 65536 -config ../config/ca.cnf -echo "generating CA private key and certificate" -openssl req -nodes -new -x509 -keyout ca-key.pem -out ca-cert.pem -days 65536 -config config/ca.cnf + echo "generating server private key and CSR" + openssl req -new -nodes -newkey $key_family -pkeyopt $argname$key_size -keyout server-key.pem -out server.csr -config ../config/server.cnf -# secp384r1 is an arbitrarily chosen curve that is supported by the default -# security policy in s2n-tls. -# https://github.com/aws/s2n-tls/blob/main/docs/USAGE-GUIDE.md#chart-security-policy-version-to-supported-curvesgroups -echo "generating server private key and CSR" -openssl req -new -nodes -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -keyout server-key.pem -out server.csr -config config/server.cnf + echo "generating client private key and CSR" + openssl req -new -nodes -newkey $key_family -pkeyopt $argname$key_size -keyout client-key.pem -out client.csr -config ../config/client.cnf -echo "generating client private key and CSR" -openssl req -new -nodes -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -keyout client-key.pem -out client.csr -config config/client.cnf + echo "generating server certificate and signing it" + openssl x509 -days 65536 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extensions req_ext -extfile ../config/server.cnf -echo "generating server certificate and signing it" -openssl x509 -days 65536 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extensions req_ext -extfile config/server.cnf + echo "generating client certificate and signing it" + openssl x509 -days 65536 -req -in client.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -extensions req_ext -extfile ../config/client.cnf -echo "generating client certificate and signing it" -openssl x509 -days 65536 -req -in client.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -extensions req_ext -extfile config/client.cnf + echo "verifying generated certificates" + openssl verify -CAfile ca-cert.pem server-cert.pem + openssl verify -CAfile ca-cert.pem client-cert.pem -echo "verifying generated certificates" -openssl verify -CAfile ca-cert.pem server-cert.pem -openssl verify -CAfile ca-cert.pem client-cert.pem + echo "cleaning up temporary files" + rm server.csr + rm client.csr + rm ca-key.pem -cat server-cert.pem ca-cert.pem > server-fullchain.pem -cat client-cert.pem ca-cert.pem > client-fullchain.pem + cd .. +} -echo "cleaning up temporary files" -rm server.csr -rm client.csr -rm ca-key.pem +if [[ $1 != "clean" ]] +then + cert-gen ec 384 ecdsa384 + cert-gen rsa 2048 rsa2048 + cert-gen rsa 3072 rsa3072 + cert-gen rsa 4096 rsa4096 +else + echo "cleaning certs" + rm -rf ecdsa*/ rsa*/ +fi -popd +popd > /dev/null diff --git a/bindings/rust/bench/certs/server-cert.pem b/bindings/rust/bench/certs/server-cert.pem deleted file mode 100644 index b3d4d0ed3ba..00000000000 --- a/bindings/rust/bench/certs/server-cert.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIUNDIVTXmgX5RATAJujllJQucIu74wDQYJKoZIhvcNAQEL -BQAwgYkxCzAJBgNVBAYTAkpQMQ4wDAYDVQQIDAVDaGliYTETMBEGA1UEBwwKQ2hp -YmEgQ2l0eTEYMBYGA1UECgwPVGVzc2llci1Bc2hwb29sMRgwFgYDVQQDDA9kZXZl -bG9wLmxvY2FsY2ExITAfBgkqhkiG9w0BCQEWEmNhQGRldmVsb3AubG9jYWxjYTAg -Fw0yMzA2MDkxNzI1MDJaGA8yMjAyMTExNDE3MjUwMlowYDELMAkGA1UEBhMCVVMx -EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDzANBgNVBAoM -BnNlcnZlcjEZMBcGA1UEAwwQc2VydmVyLmxvY2FsaG9zdDB2MBAGByqGSM49AgEG -BSuBBAAiA2IABJD8tMww7UbaLR4YF2nR4zIJuYPBvo+4ur334MfX5RSVNSheX0x8 -t6CDzSxQQhURuBZuEPgsxhKKjj9+OAq/jTX2tS/hVfeMUoCyaqoKlNCUDu0EhunH -51FINWa/ejbQw6OB7jCB6zAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYE -FPxzHMcaw/HlTkUsoljncbhHxRXxMIGzBgNVHSMEgaswgaihgY+kgYwwgYkxCzAJ -BgNVBAYTAkpQMQ4wDAYDVQQIDAVDaGliYTETMBEGA1UEBwwKQ2hpYmEgQ2l0eTEY -MBYGA1UECgwPVGVzc2llci1Bc2hwb29sMRgwFgYDVQQDDA9kZXZlbG9wLmxvY2Fs -Y2ExITAfBgkqhkiG9w0BCQEWEmNhQGRldmVsb3AubG9jYWxjYYIUd3XGeEtpUSwl -UMjPQ8cfWZyVs+4wDQYJKoZIhvcNAQELBQADggEBAAejR6XomhNxTtvKNGA7SgDh -yeoz9ZQUipu2s4Dfy7Sf3s6NGRF8pL+3RByk5yB87I8YpdfWI0pjSZK1EWVXpwe4 -26E8EwncNVpaXJeI3z4L33IWSMYE+Q1MmduN8EvBtpMRSt06vLHZ+Y+uOZbsdaOq -ioT4AP6AK7ZJ74STJWJIVMS6pNy9Do6HSAa9RX5+5fkn3gpho+Qtdhq0dRF/8RV6 -c7P79o6Q0SoK99G9WJOzwOkmXR9ytLAKq5GxS7JaxEjShWXc+v0VqEC/94iktXaM -Bh9ndJPw6NMAhWZONpNqzQIzKKsNXtGqGWzN3Ed6bNnNFQohcZnyu5AT8kf3iQA= ------END CERTIFICATE----- diff --git a/bindings/rust/bench/certs/server-fullchain.pem b/bindings/rust/bench/certs/server-fullchain.pem deleted file mode 100644 index ab5694c0383..00000000000 --- a/bindings/rust/bench/certs/server-fullchain.pem +++ /dev/null @@ -1,44 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIUNDIVTXmgX5RATAJujllJQucIu74wDQYJKoZIhvcNAQEL -BQAwgYkxCzAJBgNVBAYTAkpQMQ4wDAYDVQQIDAVDaGliYTETMBEGA1UEBwwKQ2hp -YmEgQ2l0eTEYMBYGA1UECgwPVGVzc2llci1Bc2hwb29sMRgwFgYDVQQDDA9kZXZl -bG9wLmxvY2FsY2ExITAfBgkqhkiG9w0BCQEWEmNhQGRldmVsb3AubG9jYWxjYTAg -Fw0yMzA2MDkxNzI1MDJaGA8yMjAyMTExNDE3MjUwMlowYDELMAkGA1UEBhMCVVMx -EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDzANBgNVBAoM -BnNlcnZlcjEZMBcGA1UEAwwQc2VydmVyLmxvY2FsaG9zdDB2MBAGByqGSM49AgEG -BSuBBAAiA2IABJD8tMww7UbaLR4YF2nR4zIJuYPBvo+4ur334MfX5RSVNSheX0x8 -t6CDzSxQQhURuBZuEPgsxhKKjj9+OAq/jTX2tS/hVfeMUoCyaqoKlNCUDu0EhunH -51FINWa/ejbQw6OB7jCB6zAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYE -FPxzHMcaw/HlTkUsoljncbhHxRXxMIGzBgNVHSMEgaswgaihgY+kgYwwgYkxCzAJ -BgNVBAYTAkpQMQ4wDAYDVQQIDAVDaGliYTETMBEGA1UEBwwKQ2hpYmEgQ2l0eTEY -MBYGA1UECgwPVGVzc2llci1Bc2hwb29sMRgwFgYDVQQDDA9kZXZlbG9wLmxvY2Fs -Y2ExITAfBgkqhkiG9w0BCQEWEmNhQGRldmVsb3AubG9jYWxjYYIUd3XGeEtpUSwl -UMjPQ8cfWZyVs+4wDQYJKoZIhvcNAQELBQADggEBAAejR6XomhNxTtvKNGA7SgDh -yeoz9ZQUipu2s4Dfy7Sf3s6NGRF8pL+3RByk5yB87I8YpdfWI0pjSZK1EWVXpwe4 -26E8EwncNVpaXJeI3z4L33IWSMYE+Q1MmduN8EvBtpMRSt06vLHZ+Y+uOZbsdaOq -ioT4AP6AK7ZJ74STJWJIVMS6pNy9Do6HSAa9RX5+5fkn3gpho+Qtdhq0dRF/8RV6 -c7P79o6Q0SoK99G9WJOzwOkmXR9ytLAKq5GxS7JaxEjShWXc+v0VqEC/94iktXaM -Bh9ndJPw6NMAhWZONpNqzQIzKKsNXtGqGWzN3Ed6bNnNFQohcZnyu5AT8kf3iQA= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDnTCCAoUCFHd1xnhLaVEsJVDIz0PHH1mclbPuMA0GCSqGSIb3DQEBCwUAMIGJ -MQswCQYDVQQGEwJKUDEOMAwGA1UECAwFQ2hpYmExEzARBgNVBAcMCkNoaWJhIENp -dHkxGDAWBgNVBAoMD1Rlc3NpZXItQXNocG9vbDEYMBYGA1UEAwwPZGV2ZWxvcC5s -b2NhbGNhMSEwHwYJKoZIhvcNAQkBFhJjYUBkZXZlbG9wLmxvY2FsY2EwIBcNMjMw -NjA5MTcyNTAyWhgPMjIwMjExMTQxNzI1MDJaMIGJMQswCQYDVQQGEwJKUDEOMAwG -A1UECAwFQ2hpYmExEzARBgNVBAcMCkNoaWJhIENpdHkxGDAWBgNVBAoMD1Rlc3Np -ZXItQXNocG9vbDEYMBYGA1UEAwwPZGV2ZWxvcC5sb2NhbGNhMSEwHwYJKoZIhvcN -AQkBFhJjYUBkZXZlbG9wLmxvY2FsY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDII4MvapIuBLmDoIn/jeu9xeHqw8vUiPZecg4azweLv6B/QKKQSAXF -bQA2JVCSHF7gxjHDvOFG3+Kx/RV74hKjHlp2yVMskW1r4e6WCC+duIBgR2y7xfaM -7C5XKt6DuSexHECmUiU9cG2ZRP8lx8Wclvet9Ob+iP+sx3xQJ8Y0NtLjeo5UCV5X -CAAVBUHLTypRNSsU6LFxkRjs2JxdjGLG98+ih4VNfc5lUXBJzcrCSLErnaIhvxcz -X6myHd57eCYsWEsukuGcY+59rgPkxDofK+G/PwMOMVjYeQ1HoR/Ms9+mgN88Udcm -d9IXCKZh1dmqbBZMOXZi5fjaViO8xhe1AgMBAAEwDQYJKoZIhvcNAQELBQADggEB -AKA3FgjF97cFyT0EfZfJag3g5sSq1NLqGuAvmtgaUhdqRT2vo5Uuaj+ZqxmFvEll -tb9zeQgaPksyW+wZ+Lg46qYHhwuInycqke6Sa4FKlQrydThoF9tM1zQtnJqzdX1s -gcm731nR2O3lYsrfNRasOeB11rk4a14cJRwWk57d8qt/nLwmPMt9dc4IsCmqQw6l -s0f49lX/PmEenAaO5hfVI7XcasT260L4/NlFA2HzrbGdO+o2aCrSfA5+Qt0PMsjZ -H2P+1/JefgRyMDG8hk0ev2aoqkGBvZrSJyCI/+EZoJRuS1qPI3S+kHGXkJ3tF4i+ -IeTBeNaFh9ayc/UoUPyved0= ------END CERTIFICATE----- diff --git a/bindings/rust/bench/certs/server-key.pem b/bindings/rust/bench/certs/server-key.pem deleted file mode 100644 index 20083acc9ce..00000000000 --- a/bindings/rust/bench/certs/server-key.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCSl0T2qNjybO7rcLvx -++vXvjPk98d+YXxGKH+voS+VynS8eOyjcVjJVRYCetXZAlihZANiAASQ/LTMMO1G -2i0eGBdp0eMyCbmDwb6PuLq99+DH1+UUlTUoXl9MfLegg80sUEIVEbgWbhD4LMYS -io4/fjgKv4019rUv4VX3jFKAsmqqCpTQlA7tBIbpx+dRSDVmv3o20MM= ------END PRIVATE KEY----- diff --git a/bindings/rust/bench/historical-perf/bench-past.sh b/bindings/rust/bench/historical-perf/bench-past.sh new file mode 100755 index 00000000000..fc18ed54d2f --- /dev/null +++ b/bindings/rust/bench/historical-perf/bench-past.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# immediately bail if any command fails +set -e + +# suppress stdout and most cargo warnings +exec >/dev/null +export CARGO_TERM_QUIET=true +export RUSTFLAGS=-Awarnings + +# go to s2n-tls/bindings/rust/bench/ +pushd "$(dirname "$0")"/../ +bench_path="$(pwd)" + +# delete past runs +rm -rf target/historical-perf + +# make Cargo.toml point s2n-tls to the cloned old version +sed -i "s|s2n-tls = .*|s2n-tls = { path = \"target/s2n-tls/bindings/rust/s2n-tls\" }|" Cargo.toml + +# ensure Cargo.toml gets changed back on exit; retains original exit status +trap "{ status=$?; sed -i 's|s2n-tls = .*|s2n-tls = { path = \"../s2n-tls\" }|' $bench_path/Cargo.toml; exit $status; }" EXIT + +# clone copy of repo to target/s2n-tls +echo "cloning repo" >&2 +mkdir -p target +cd target +rm -rf s2n-tls +git clone --quiet https://github.com/aws/s2n-tls +cd s2n-tls/bindings/rust/ +copied_bindings_path="$(pwd)" + +# get list of tags sorted newest to oldest +sorted_tags="$(git tag -l | sort -rV)" + +# last tag we want is v1.3.16, get line number of v1.3.16 in sorted_tags +line_num_last_tag=$(echo "$sorted_tags" | grep "v1.3.16" --line-number | head -n 1 | cut -d":" -f1) + +# loop through all tags in order up to v1.3.16 +for tag in $(echo "$sorted_tags" | head -$line_num_last_tag) +do + ( + # go to s2n-tls/bindings/rust/ inside copied repo + cd $copied_bindings_path + + echo "checkout tag $tag" >&2 + git checkout $tag --quiet + + echo "generating rust bindings" >&2 + # if generate.sh fails, exit out of block + ./generate.sh || exit 1 + + echo "running cargo bench and saving results" >&2 + cd $bench_path + rm -rf target/criterion + cargo bench --features historical-perf --no-fail-fast + + # cache criterion outputs from this bench into target/historical-perf + for bench_group in $(ls target/criterion | grep -v "report") + do + mkdir -p target/historical-perf/$bench_group/ + cp target/criterion/$bench_group/s2n-tls/new/estimates.json target/historical-perf/$bench_group/$tag.json + done + ) || echo "failed, trying next tag" + echo +done + +# graph results +cd $bench_path +cargo run --release --bin graph_perf + +popd diff --git a/bindings/rust/bench/images/historical-perf-handshake.svg b/bindings/rust/bench/images/historical-perf-handshake.svg new file mode 100644 index 00000000000..79ac67a2507 --- /dev/null +++ b/bindings/rust/bench/images/historical-perf-handshake.svg @@ -0,0 +1,424 @@ + + + +Performance of handshake by version since Jun 2022 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Time + + +Version + + + + + + + + + + + + + + + + + + + + + + + +0 ms + + + +2 ms + + + +4 ms + + + +6 ms + + + + +1.3.16 + + + +1.3.18 + + + +1.3.20 + + + +1.3.22 + + + +1.3.24 + + + +1.3.26 + + + +1.3.28 + + + +1.3.30 + + + +1.3.32 + + + +1.3.34 + + + +1.3.36 + + + +1.3.38 + + + +1.3.40 + + + +1.3.42 + + + +1.3.44 + + + +1.3.46 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +handshake-MutualAuth-SECP256R1 + + +handshake-ServerAuth-X25519 + + +handshake-MutualAuth-X25519 + + +handshake-ServerAuth-SECP256R1 + + + + + + diff --git a/bindings/rust/bench/images/historical-perf-throughput.svg b/bindings/rust/bench/images/historical-perf-throughput.svg new file mode 100644 index 00000000000..46aeb36c475 --- /dev/null +++ b/bindings/rust/bench/images/historical-perf-throughput.svg @@ -0,0 +1,341 @@ + + + +Performance of throughput by version since Jun 2022 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Throughput + + +Version + + + + + + + + + + + + + + + + + + + + + + +0 GB/s + + + +0.5 GB/s + + + +1 GB/s + + + + +1.3.16 + + + +1.3.18 + + + +1.3.20 + + + +1.3.22 + + + +1.3.24 + + + +1.3.26 + + + +1.3.28 + + + +1.3.30 + + + +1.3.32 + + + +1.3.34 + + + +1.3.36 + + + +1.3.38 + + + +1.3.40 + + + +1.3.42 + + + +1.3.44 + + + +1.3.46 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +throughput-AES_128_GCM_SHA256 + + +throughput-AES_256_GCM_SHA384 + + + + diff --git a/bindings/rust/bench/install-aws-lc.sh b/bindings/rust/bench/install-aws-lc.sh new file mode 100755 index 00000000000..e17c180e1c9 --- /dev/null +++ b/bindings/rust/bench/install-aws-lc.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Sets up bench crate to use aws-lc for either s2n-tls or rustls if desired +# To run with aws-lc, use any cargo command with: +# --config aws-lc-config/s2n.toml +# --config aws-lc-config/rustls.toml +# or both + +set -e + +# go to bench directory +pushd "$(dirname "$0")" > /dev/null +bench_dir="$(pwd)" + + + +# ----- rustls ----- + +# clone rustls to bench/target/rustls and checkout compatible version +rm -rf target/rustls +git clone https://github.com/rustls/rustls target/rustls +cd target/rustls +git checkout 'v/0.21.5' + +# go to dir with rustls crate +cd rustls +rustls_dir="$(pwd)" + +# change rustls to use aws-lc-rs +sed -i 's|ring = .*|ring = { package = "aws-lc-rs" }|' Cargo.toml + + + +# ----- s2n-tls ----- + +# put all build artifacts in target +s2n_tls_build_dir="$bench_dir"/target/s2n-tls-build +aws_lc_dir="$bench_dir"/target/aws-lc + +# go to repo directory +cd "$bench_dir"/../../../ > /dev/null +repo_dir="$(pwd)" + +# if libs2n not found, build it +if [ ! -e "$s2n_tls_build_dir"/lib/libs2n.a ] +then + # if aws-lc not found, build it + if [ ! -e "$aws_lc_dir"/install/lib/libcrypto.a ] + then + # clone fresh aws-lc + cd "$bench_dir" + rm -rf target/aws-lc + git clone --depth=1 https://github.com/aws/aws-lc target/aws-lc + cd target/aws-lc + + # build and install aws-lc + cmake -B build -DCMAKE_INSTALL_PREFIX="$aws_lc_dir"/install -DBUILD_TESTING=OFF -DBUILD_LIBSSL=OFF -DCMAKE_BUILD_TYPE=Release + cmake --build ./build -j $(nproc) + make -C build install + else + echo "using libcrypto.a at target/aws-lc/install/lib" + fi + + # clean up directories + rm -rf "$s2n_tls_build_dir" + mkdir -p "$s2n_tls_build_dir" + + # build and install s2n-tls + cd "$repo_dir" + cmake . -B "$s2n_tls_build_dir" -DCMAKE_PREFIX_PATH="$aws_lc_dir"/install -DS2N_INTERN_LIBCRYPTO=ON -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release + cmake --build "$s2n_tls_build_dir" -j $(nproc) +else + echo "using libs2n.a at target/s2n-tls-build/lib" +fi + +# force rebuild of s2n-tls-sys and benches +rm -rf ../target/release target/release + + + +popd > /dev/null diff --git a/bindings/rust/bench/memory/bench-memory.sh b/bindings/rust/bench/memory/bench-memory.sh new file mode 100755 index 00000000000..7ee788cdf10 --- /dev/null +++ b/bindings/rust/bench/memory/bench-memory.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Use Valgrind and Massif to heap profile the memory taken by different TLS libraries +# Uses Valgrind monitor commands to take snapshots of heap size while making connections + +# Snapshots get stored in target memory/[library-name]/ as [number].snapshot + +set -e + +pushd "$(dirname "$0")"/.. > /dev/null + +cargo build --release --bin memory "$@" + +valgrind --tool=massif --depth=1 --massif-out-file="target/memory/massif.out" --time-unit=ms target/release/memory +rm target/memory/massif.out + +cargo run --release --bin graph_memory "$@" + +popd > /dev/null diff --git a/bindings/rust/bench/src/bin/graph_memory.rs b/bindings/rust/bench/src/bin/graph_memory.rs new file mode 100644 index 00000000000..20c0fd0789d --- /dev/null +++ b/bindings/rust/bench/src/bin/graph_memory.rs @@ -0,0 +1,118 @@ +use plotters::{ + prelude::{ + ChartBuilder, IntoDrawingArea, IntoSegmentedCoord, LabelAreaPosition, Rectangle, + SVGBackend, SegmentValue, + }, + style::{AsRelative, Color, IntoFont, Palette, Palette99, RGBAColor, WHITE}, +}; +use std::{ + collections::HashMap, + error::Error, + fs::{read_dir, read_to_string}, +}; + +struct Stats { + mean: f64, + stderr: f64, +} + +fn get_bytes_from_snapshot(name: &str, i: i32) -> i32 { + // number of bytes in snapshot starts on 8th line, 12th character + read_to_string(format!("target/memory/{name}/{i}.snapshot")) + .unwrap() + .lines() + .nth(7) + .unwrap()[11..] + .parse() + .unwrap() +} + +/// Get the difference in bytes between two snapshots, which is memory of the +/// `i`th TlsConnPair (client and server) +fn get_bytes_diff(name: &str, i: i32) -> i32 { + get_bytes_from_snapshot(name, i + 1) - get_bytes_from_snapshot(name, i) +} + +fn get_memory_data(name: &str) -> Stats { + let data: Vec = (0..100).map(|i| get_bytes_diff(name, i) as f64).collect(); + let mean = data.iter().sum::() / (data.len() as f64); + let variance: f64 = + data.iter().map(|x| (x - mean) * (x - mean)).sum::() / ((data.len() - 1) as f64); + let stdev = variance.sqrt(); + let stderr = stdev / (data.len() as f64).sqrt(); + + Stats { mean, stderr } +} + +fn main() -> Result<(), Box> { + // get data from each directory in target/memory + // map bench name to its memory stats + let mut stats: HashMap = Default::default(); + for dir_entry in read_dir("target/memory")? { + let dir_path = dir_entry?.path(); + let dir_name = dir_path.file_name().unwrap().to_str().unwrap().to_string(); + + if dir_name != "xtree" { + stats.insert(dir_name.clone(), get_memory_data(&dir_name)); + } + } + + let num_bars = stats.len(); + let x_labels: Vec = stats.iter().map(|kv| kv.0.clone()).collect(); + let max_mem = stats + .iter() + .max_by(|a, b| f64::total_cmp(&a.1.mean, &b.1.mean)) + .unwrap() + .1 + .mean; + + let drawing_area = SVGBackend::new("memory/memory.svg", (1000, 500)).into_drawing_area(); + drawing_area.fill(&WHITE)?; + + let mut ctx = ChartBuilder::on(&drawing_area) + .caption( + "Memory usage of a connection pair", + ("sans-serif", 30).into_font(), + ) + .set_label_area_size(LabelAreaPosition::Left, (15).percent()) // axes padding + .set_label_area_size(LabelAreaPosition::Bottom, (6).percent()) + .build_cartesian_2d( + (0..num_bars - 1).into_segmented(), + 0.0..(1.1 * max_mem), // upper y bound on plot is 1.1 * y_max + )?; + + let axis_label_style = ("sans-serif", 18).into_font(); + + ctx.configure_mesh() + .light_line_style(RGBAColor(235, 235, 235, 1.0)) // change gridline color + .bold_line_style(RGBAColor(225, 225, 225, 1.0)) + .x_labels(num_bars) + .x_label_formatter(&|x| { + // change axis labels to name of bar + let x = match *x { + SegmentValue::CenterOf(x) => x, + _ => 0, + }; + x_labels.get(x).unwrap().to_string() + }) + .x_label_style(axis_label_style.clone()) + .y_desc("Memory (kB)") + .y_labels(10) // max number of labels on y axis + .y_label_formatter(&|y| format!("{} kB", y / 1000.0)) + .y_label_style(axis_label_style) + .draw()?; + + // draw bars + // x coord is index of bench name in x_labels + ctx.draw_series(stats.iter().enumerate().map(|(i, (_name, stats))| { + // define each bar as a Rectangle + let x0 = SegmentValue::Exact(i); + let x1 = SegmentValue::Exact(i + 1); + let color = Palette99::pick(i).filled(); + let mut bar = Rectangle::new([(x0, 0.0), (x1, stats.mean)], color); + bar.set_margin(0, 0, 100, 100); // spacing between bars + bar + }))?; + + Ok(()) +} diff --git a/bindings/rust/bench/src/bin/graph_perf.rs b/bindings/rust/bench/src/bin/graph_perf.rs new file mode 100644 index 00000000000..dda56d59bf6 --- /dev/null +++ b/bindings/rust/bench/src/bin/graph_perf.rs @@ -0,0 +1,303 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use plotters::{ + prelude::{ + BindKeyPoints, ChartBuilder, ErrorBar, IntoDrawingArea, LabelAreaPosition, Rectangle, + SVGBackend, SeriesLabelPosition, + }, + series::LineSeries, + style::{AsRelative, Color, IntoFont, Palette, Palette99, RGBAColor, BLACK, WHITE}, +}; +use semver::Version; +use serde_json::Value; +use std::{ + collections::{BTreeSet, HashMap}, + fs::{read_dir, read_to_string}, + path::Path, +}; + +struct Stats { + mean: f64, + stderr: f64, +} + +struct VersionDataPoint { + version: Version, // x coordinate + mean: f64, // y coordinate + stderr: f64, // y error bar +} + +struct VersionDataSeries { + name: String, // ex. throughput-AES_128_GCM_SHA256 + data: Vec, +} + +struct DataPoint { + x: i32, + y: f64, + y_bar: f64, +} + +struct DataSeries { + name: String, + data: Vec, +} + +/// Get the relevant stats in a given JSON bench output +fn process_single_json(path: &Path) -> Stats { + let json_str = read_to_string(path).unwrap(); + let json_value: Value = serde_json::from_str(json_str.as_str()).unwrap(); + let stats = json_value.get("mean").unwrap(); + Stats { + mean: stats.get("point_estimate").unwrap().as_f64().unwrap(), + stderr: stats.get("standard_error").unwrap().as_f64().unwrap(), + } +} + +/// Get data from directory of Criterion json outputs, given directory path +/// Outputs a Vec of (version, mean, stderr) sorted by version +fn parse_bench_group_data(path: &Path) -> Vec { + let mut data: Vec = read_dir(path) + .unwrap() + .map(|dir_entry_res| { + let path = dir_entry_res.unwrap().path(); + let stats = process_single_json(&path); + let tag = path.file_stem().unwrap().to_str().unwrap(); + let version = Version::parse(&tag[1..]).unwrap(); + VersionDataPoint { + version, + mean: stats.mean, + stderr: stats.stderr, + } + }) + .collect(); + data.sort_by(|data_point_1, data_point_2| data_point_1.version.cmp(&data_point_2.version)); + data +} + +/// Gets data from all bench groups given a prefix (ex. "handshake") for the bench group names +fn get_all_data(prefix: &str) -> Vec { + read_dir("target/historical-perf") + .unwrap() + .map(|dir_entry_res| dir_entry_res.unwrap().path()) + .filter(|path| { + // get all paths starting with prefix + path.file_name() + .unwrap() + .to_str() + .unwrap() + .starts_with(prefix) + }) + .map(|path| { + // get data in each directory + VersionDataSeries { + name: path.file_name().unwrap().to_string_lossy().into_owned(), + data: parse_bench_group_data(&path), + } + }) + .collect() +} + +fn get_unique_versions(data: &[VersionDataSeries]) -> BTreeSet { + data.iter() + .flat_map(|data_series| { + data_series + .data + .iter() + .map(|version_data_point| version_data_point.version.clone()) + }) + .collect() +} + +/// Converts all VersionDataSeries in version_data to DataSeries +fn convert_to_data_series( + version_data: Vec, + version_to_x: &HashMap<&Version, i32>, +) -> Vec { + version_data + .into_iter() + .map(|version_data_series| DataSeries { + name: version_data_series.name, + data: version_data_series + .data + .into_iter() + .map(|version_data_point| DataPoint { + // map VersionDataPoints to DataPoints + x: version_to_x[&&version_data_point.version], + y: version_data_point.mean, + y_bar: version_data_point.stderr * 1.96, // 95% confidence interval + }) + .collect(), + }) + .collect() +} + +/// Plots given DataSeries with given chart parameters +fn plot_data String, G: Fn(&f64) -> String>( + data: &[DataSeries], + bench_name: &str, + x_label_formatter: &F, + y_label: &str, + y_label_formatter: &G, +) { + // get x_max and y_max for plotting range + let x_max = data + .iter() + .flat_map(|data_series| data_series.data.iter().map(|data_point| data_point.x)) + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + let y_max = data + .iter() + .flat_map(|data_series| data_series.data.iter().map(|data_point| data_point.y)) + .max_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap(); + + // setup plotting + let path = format!("images/historical-perf-{bench_name}.svg"); + let drawing_area = SVGBackend::new(&path, (1000, 500)).into_drawing_area(); + drawing_area.fill(&WHITE).unwrap(); + + let mut ctx = ChartBuilder::on(&drawing_area) + .caption( + format!("Performance of {bench_name} by version since Jun 2022"), + ("sans-serif", 30).into_font(), + ) + .set_label_area_size(LabelAreaPosition::Left, (17).percent()) // axes padding + .set_label_area_size(LabelAreaPosition::Bottom, (11).percent()) + .build_cartesian_2d( + // bounds for plot + // plot every other x coord starting from 1 (not 0 which is default) + (0..(x_max + 1)).with_key_points((1..(x_max + 1)).step_by(2).collect()), + 0.0..(1.2 * y_max), + ) + .unwrap(); + + let axis_label_style = ("sans-serif", 18).into_font(); + + ctx.configure_mesh() + .light_line_style(RGBAColor(235, 235, 235, 1.0)) // gridline color + .bold_line_style(RGBAColor(225, 225, 225, 1.0)) + .x_desc("Version") // axis labels + .x_labels(20) // max number of labels + .x_label_style(axis_label_style.clone()) + .x_label_formatter(x_label_formatter) + .y_desc(y_label) + .y_labels(5) + .y_label_formatter(y_label_formatter) + .y_label_style(axis_label_style) + .draw() + .unwrap(); + + // go through each DataSeries and plot them + for (i, data_series) in data.iter().enumerate() { + // remove data that returned error while benching + // heuristic: times < 1% of y_max are invalid/had error + let filtered_data = data_series + .data + .iter() + .filter(|data_point| data_point.y > 0.01 * y_max) + .collect::>(); + + let color = Palette99::pick(i); + + // draw error bars + ctx.draw_series(filtered_data.iter().map(|data_point| { + ErrorBar::new_vertical( + data_point.x, + data_point.y - data_point.y_bar, + data_point.y, + data_point.y + data_point.y_bar, + &color, + 3, + ) + })) + .unwrap(); + + // draw lines with legend entry + ctx.draw_series(LineSeries::new( + filtered_data + .iter() + .map(|data_point| (data_point.x, data_point.y)), + color.stroke_width(2), + )) + .unwrap() + .label(&data_series.name) + .legend(move |(x, y)| Rectangle::new([(x, y - 5), (x + 10, y + 5)], color.filled())); + } + + // enable legend + ctx.configure_series_labels() + .position(SeriesLabelPosition::LowerRight) + .margin(10) + .border_style(BLACK) + .background_style(WHITE) + .draw() + .unwrap(); +} + +fn main() { + let handshake_data = get_all_data("handshake"); + let throughput_data = get_all_data("throughput"); + + // combine all versions present in handshake and throughput data + // also fill in missing version v1.3.15 and v1.3.30-v1.3.37 + let mut versions = get_unique_versions(&handshake_data); + versions.extend(get_unique_versions(&throughput_data).into_iter()); + versions.extend((15..16).chain(30..38).map(|p| Version::new(1, 3, p))); + let versions = versions.into_iter().collect::>(); + + // map versions to x coordinates + let version_to_x = versions + .iter() + .enumerate() + .map(|(i, version)| (version, i as i32)) + .collect::>(); + + // convert from Vec to Vec for plotting + let handshake_data: Vec = convert_to_data_series(handshake_data, &version_to_x); + let mut throughput_data = convert_to_data_series(throughput_data, &version_to_x); + + // convert data from ns to transfer of 100KB of data -> bytes/s throughput + throughput_data = throughput_data + .into_iter() + .map(|data_series| { + const TRANSFER_SIZE: f64 = 1e5; + const NANO_SIZE: f64 = 1e-9; + DataSeries { + name: data_series.name, + data: data_series + .data + .into_iter() + .map(|data_point| { + let mean_throughput = TRANSFER_SIZE / (data_point.y * NANO_SIZE); + let stderr_throughput = mean_throughput + - TRANSFER_SIZE / ((data_point.y + data_point.y_bar) * NANO_SIZE); + DataPoint { + x: data_point.x, + y: mean_throughput, + y_bar: stderr_throughput, + } + }) + .collect(), + } + }) + .collect(); + + let x_label_formatter = |x: &i32| format!("{}", versions[*x as usize]); + + plot_data( + &handshake_data, + "handshake", + &x_label_formatter, + "Time", + &|y| format!("{} ms", y / 1e6), + ); + plot_data( + &throughput_data, + "throughput", + &x_label_formatter, + "Throughput", + &|y| format!("{} GB/s", y / 1e9), + ); +} diff --git a/bindings/rust/bench/src/bin/memory.rs b/bindings/rust/bench/src/bin/memory.rs new file mode 100644 index 00000000000..6a09b548b9e --- /dev/null +++ b/bindings/rust/bench/src/bin/memory.rs @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use bench::{ + harness::ConnectedBuffer, CryptoConfig, HandshakeType, OpenSslConnection, RustlsConnection, + S2NConnection, TlsConnPair, TlsConnection, +}; +use std::{fs::create_dir_all, path::Path}; + +fn memory_bench(dir_name: &str) { + println!("testing {dir_name}"); + + if !Path::new(&format!("target/memory/{dir_name}")).is_dir() { + create_dir_all(format!("target/memory/{dir_name}")).unwrap(); + } + + let mut conn_pairs = Vec::new(); + conn_pairs.reserve(100); + + // reserve space for buffers before benching + let mut buffers = Vec::new(); + buffers.reserve(100); + for _ in 0..100 { + buffers.push(ConnectedBuffer::new()); + } + + // handshake one harness to initalize libraries + let mut conn_pair = TlsConnPair::::default(); + conn_pair.handshake().unwrap(); + + // tell massif to take initial memory snapshot + crabgrind::monitor_command(format!("snapshot target/memory/{dir_name}/0.snapshot")).unwrap(); + + // make and handshake 100 connection pairs + // memory usage stabilizes after first few handshakes + for i in 1..101 { + // put new harness directly into harness vec + conn_pairs.push( + TlsConnPair::::new( + CryptoConfig::default(), + HandshakeType::default(), + buffers.pop().unwrap(), // take ownership of buffer + ) + .unwrap(), + ); + + // handshake last harness added + conn_pairs + .as_mut_slice() + .last_mut() + .unwrap() + .handshake() + .unwrap(); + + // take memory snapshot + crabgrind::monitor_command(format!("snapshot target/memory/{dir_name}/{i}.snapshot")) + .unwrap(); + } +} + +fn main() { + assert!(!cfg!(debug_assertions), "need to run in release mode"); + + memory_bench::("s2n-tls"); + memory_bench::("rustls"); + memory_bench::("openssl"); +} diff --git a/bindings/rust/bench/src/harness.rs b/bindings/rust/bench/src/harness.rs index b53a1af5b10..3e0e9d6e1df 100644 --- a/bindings/rust/bench/src/harness.rs +++ b/bindings/rust/bench/src/harness.rs @@ -5,22 +5,81 @@ use std::{ cell::RefCell, collections::VecDeque, error::Error, + fmt::Debug, fs::read_to_string, io::{ErrorKind, Read, Write}, rc::Rc, }; +use strum::EnumIter; -pub fn read_to_bytes(path: &str) -> Vec { - read_to_string(path).unwrap().into_bytes() +#[derive(Clone, Copy, EnumIter)] +pub enum PemType { + ServerKey, + ServerCertChain, + ClientKey, + ClientCertChain, + CACert, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +impl PemType { + fn get_filename(&self) -> &str { + match self { + PemType::ServerKey => "server-key.pem", + PemType::ServerCertChain => "server-cert.pem", + PemType::ClientKey => "client-key.pem", + PemType::ClientCertChain => "client-cert.pem", + PemType::CACert => "ca-cert.pem", + } + } +} + +#[derive(Clone, Copy, Default, EnumIter)] +pub enum SigType { + Rsa2048, + Rsa3072, + Rsa4096, + #[default] + Ecdsa384, +} + +impl SigType { + pub fn get_dir_name(&self) -> &str { + match self { + SigType::Rsa2048 => "rsa2048", + SigType::Rsa3072 => "rsa3072", + SigType::Rsa4096 => "rsa4096", + SigType::Ecdsa384 => "ecdsa384", + } + } +} + +impl Debug for SigType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.get_dir_name()) + } +} + +pub fn get_cert_path(pem_type: PemType, sig_type: SigType) -> String { + format!( + "certs/{}/{}", + sig_type.get_dir_name(), + pem_type.get_filename() + ) +} + +pub fn read_to_bytes(pem_type: PemType, sig_type: SigType) -> Vec { + read_to_string(get_cert_path(pem_type, sig_type)) + .unwrap() + .into_bytes() +} + +#[derive(Clone, Copy)] pub enum Mode { Client, Server, } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, Default, EnumIter, Eq, PartialEq)] pub enum HandshakeType { #[default] ServerAuth, @@ -30,100 +89,272 @@ pub enum HandshakeType { // these parameters were the only ones readily usable for all three libaries: // s2n-tls, rustls, and openssl #[allow(non_camel_case_types)] -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Default, EnumIter, Eq, PartialEq)] pub enum CipherSuite { #[default] AES_128_GCM_SHA256, AES_256_GCM_SHA384, } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub enum ECGroup { - SECP256R1, +#[derive(Clone, Copy, Default, EnumIter)] +pub enum KXGroup { + Secp256R1, #[default] X25519, } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +impl Debug for KXGroup { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Secp256R1 => "secp256r1", + Self::X25519 => "x25519", + } + ) + } +} + +#[derive(Clone, Copy, Debug, Default)] pub struct CryptoConfig { pub cipher_suite: CipherSuite, - pub ec_group: ECGroup, + pub kx_group: KXGroup, + pub sig_type: SigType, +} + +impl CryptoConfig { + pub fn new(cipher_suite: CipherSuite, kx_group: KXGroup, sig_type: SigType) -> Self { + Self { + cipher_suite, + kx_group, + sig_type, + } + } } -pub trait TlsBenchHarness: Sized { - /// Default harness - fn default() -> Result> { - Self::new(Default::default(), Default::default()) +pub trait TlsConnection: Sized { + /// Library-specific config struct + type Config; + + /// Name of the connection type + fn name() -> String; + + /// Default connection (client or server) + fn default(mode: Mode) -> Result> { + Self::new( + mode, + CryptoConfig::default(), + HandshakeType::default(), + ConnectedBuffer::default(), + ) } + /// Make a config with given parameters + fn make_config( + mode: Mode, + crypto_config: CryptoConfig, + handshake_type: HandshakeType, + ) -> Result>; + + /// Make connection from existing config and buffer + fn new_from_config( + config: &Self::Config, + connected_buffer: ConnectedBuffer, + ) -> Result>; + /// Initialize buffers, configs, and connections (pre-handshake) fn new( + mode: Mode, crypto_config: CryptoConfig, handshake_type: HandshakeType, - ) -> Result>; + buffer: ConnectedBuffer, + ) -> Result> { + Self::new_from_config( + &Self::make_config(mode, crypto_config, handshake_type)?, + buffer, + ) + } - /// Run handshake on initialized connection - /// Returns error if handshake has already completed + /// Run one handshake step: receive msgs from other connection, process, and send new msgs fn handshake(&mut self) -> Result<(), Box>; - /// Checks if handshake is finished for both client and server fn handshake_completed(&self) -> bool; - /// Get negotiated cipher suite fn get_negotiated_cipher_suite(&self) -> CipherSuite; - /// Get whether or negotiated version is TLS1.3 fn negotiated_tls13(&self) -> bool; - /// Send application data from connection in harness pair - fn send(&mut self, sender: Mode, data: &[u8]) -> Result<(), Box>; + /// Send application data to ConnectedBuffer + fn send(&mut self, data: &[u8]) -> Result<(), Box>; + + /// Read application data from ConnectedBuffer + fn recv(&mut self, data: &mut [u8]) -> Result<(), Box>; + + /// Shrink buffers owned by the connection + fn shrink_connection_buffers(&mut self); - /// Receive application data sent to connection in harness pair - fn recv(&mut self, receiver: Mode, data: &mut [u8]) -> Result<(), Box>; + /// Clear and shrink buffers used for IO with another connection + fn shrink_connected_buffer(&mut self); - /// Send data from client to server and then from server to client - fn round_trip_transfer(&mut self, data: &mut [u8]) -> Result<(), Box> { + /// Get reference to internal connected buffer + fn connected_buffer(&self) -> &ConnectedBuffer; +} + +pub struct TlsConnPair { + client: C, + server: S, +} + +impl Default for TlsConnPair { + fn default() -> Self { + Self::new(Default::default(), Default::default(), Default::default()).unwrap() + } +} + +impl TlsConnPair { + /// Wrap two TlsConnections into a TlsConnPair + pub fn wrap(client: C, server: S) -> Self { + assert!( + client.connected_buffer() == &server.connected_buffer().clone_inverse(), + "connected buffers don't match" + ); + Self { client, server } + } + + /// Take back ownership of individual connections in the TlsConnPair + pub fn split(self) -> (C, S) { + (self.client, self.server) + } + + /// Initialize buffers, configs, and connections (pre-handshake) + pub fn new( + crypto_config: CryptoConfig, + handshake_type: HandshakeType, + connected_buffer: ConnectedBuffer, + ) -> Result> { + Ok(Self { + client: C::new( + Mode::Client, + crypto_config, + handshake_type, + connected_buffer.clone_inverse(), + )?, + server: S::new( + Mode::Server, + crypto_config, + handshake_type, + connected_buffer, + )?, + }) + } + + /// Run handshake on connections + /// Two round trips are needed for the server to receive the Finished message + /// from the client and be ready to send data + pub fn handshake(&mut self) -> Result<(), Box> { + for _ in 0..2 { + self.client.handshake()?; + self.server.handshake()?; + } + Ok(()) + } + + /// Checks if handshake is finished for both client and server + pub fn handshake_completed(&self) -> bool { + self.client.handshake_completed() && self.server.handshake_completed() + } + + pub fn get_negotiated_cipher_suite(&self) -> CipherSuite { + assert!(self.handshake_completed()); + assert!( + self.client.get_negotiated_cipher_suite() == self.server.get_negotiated_cipher_suite() + ); + self.client.get_negotiated_cipher_suite() + } + + pub fn negotiated_tls13(&self) -> bool { + self.client.negotiated_tls13() && self.server.negotiated_tls13() + } + + /// Send data from client to server, and then from server to client + pub fn round_trip_transfer(&mut self, data: &mut [u8]) -> Result<(), Box> { // send data from client to server - self.send(Mode::Client, data)?; - self.recv(Mode::Server, data)?; + self.client.send(data)?; + self.server.recv(data)?; // send data from server to client - self.send(Mode::Server, data)?; - self.recv(Mode::Client, data)?; + self.server.send(data)?; + self.client.recv(data)?; Ok(()) } + + /// Shrink buffers owned by the connections + pub fn shrink_connection_buffers(&mut self) { + self.client.shrink_connection_buffers(); + self.server.shrink_connection_buffers(); + } + + /// Clear and shrink buffers used for IO between the connections + pub fn shrink_connected_buffers(&mut self) { + self.client.shrink_connected_buffer(); + self.server.shrink_connected_buffer(); + } } /// Wrapper of two shared buffers to pass as stream /// This wrapper `read()`s into one buffer and `write()`s to another -#[derive(Clone)] +/// `Rc>>` allows sharing of references to the buffers for two connections +#[derive(Clone, Eq)] pub struct ConnectedBuffer { recv: Rc>>, send: Rc>>, } +impl PartialEq for ConnectedBuffer { + /// ConnectedBuffers are equal if and only if they point to the same VecDeques + fn eq(&self, other: &ConnectedBuffer) -> bool { + Rc::ptr_eq(&self.recv, &other.recv) && Rc::ptr_eq(&self.send, &other.send) + } +} + impl ConnectedBuffer { /// Make a new struct with new internal buffers pub fn new() -> Self { - Self { - recv: Rc::new(RefCell::new(VecDeque::new())), - send: Rc::new(RefCell::new(VecDeque::new())), - } + let recv = Rc::new(RefCell::new(VecDeque::new())); + let send = Rc::new(RefCell::new(VecDeque::new())); + + // prevent (potentially slow) resizing of buffers for small data transfers, + // like with handshake + recv.borrow_mut().reserve(10000); + send.borrow_mut().reserve(10000); + + Self { recv, send } } - /// Make a new struct that shares internal buffers but swapped, ex. - /// `write()` writes to the buffer that the inverse `read()`s from + + /// Makes a new ConnectedBuffer that shares internal buffers but swapped, + /// ex. `write()` writes to the buffer that the inverse `read()`s from pub fn clone_inverse(&self) -> Self { Self { - recv: Rc::clone(&self.send), - send: Rc::clone(&self.recv), + recv: self.send.clone(), + send: self.recv.clone(), } } + + /// Clears and shrinks buffers + pub fn shrink(&mut self) { + self.recv.borrow_mut().clear(); + self.recv.borrow_mut().shrink_to_fit(); + self.send.borrow_mut().clear(); + self.send.borrow_mut().shrink_to_fit(); + } } impl Read for ConnectedBuffer { fn read(&mut self, dest: &mut [u8]) -> Result { - match self.recv.borrow_mut().read(dest) { + let res = self.recv.borrow_mut().read(dest); + match res { // rustls expects WouldBlock on read of length 0 Ok(0) => Err(std::io::Error::new(ErrorKind::WouldBlock, "blocking")), Ok(len) => Ok(len), @@ -141,60 +372,86 @@ impl Write for ConnectedBuffer { } } +impl Default for ConnectedBuffer { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] -macro_rules! test_tls_bench_harnesses { - ($($lib_name:ident: $harness_type:ty,)*) => { - $( - mod $lib_name { - use super::*; - use CipherSuite::*; - use ECGroup::*; - use HandshakeType::*; - - #[test] - fn test_handshake_config() { - for handshake_type in [ServerAuth, MutualAuth] { - for cipher_suite in [AES_128_GCM_SHA256, AES_256_GCM_SHA384] { - for ec_group in [SECP256R1, X25519] { - let crypto_config = CryptoConfig { cipher_suite: cipher_suite.clone(), ec_group: ec_group.clone() }; - let mut harness = <$harness_type>::new(crypto_config, handshake_type).unwrap(); - - assert!(!harness.handshake_completed()); - harness.handshake().unwrap(); - assert!(harness.handshake_completed()); - - assert!(harness.negotiated_tls13()); - assert_eq!(cipher_suite, harness.get_negotiated_cipher_suite()); - } - } - } +mod tests { + use super::*; + use crate::{OpenSslConnection, RustlsConnection, S2NConnection, TlsConnPair}; + use std::path::Path; + use strum::IntoEnumIterator; + + #[test] + fn test_cert_paths_valid() { + for pem_type in PemType::iter() { + for sig_type in SigType::iter() { + assert!( + Path::new(&get_cert_path(pem_type, sig_type)).exists(), + "cert not found" + ); } + } + } - #[test] - fn test_transfer() { - // use a large buffer to test across TLS record boundaries - let mut buf = [0x56u8; 1000000]; - let (mut harness, mut crypto_config); - for cipher_suite in [AES_128_GCM_SHA256, AES_256_GCM_SHA384] { - crypto_config = CryptoConfig { cipher_suite: cipher_suite, ec_group: Default::default() }; - harness = <$harness_type>::new(crypto_config, Default::default()).unwrap(); - harness.handshake().unwrap(); - harness.round_trip_transfer(&mut buf).unwrap(); + #[test] + fn test_all() { + test_type::(); + test_type::(); + test_type::(); + } + + fn test_type() { + eprintln!("{} client --- {} server", C::name(), S::name()); + eprintln!("testing handshake..."); + test_handshake_configs::(); + eprintln!("testing transfer..."); + test_transfer::(); + eprintln!(); + } + + fn test_handshake_configs() { + for handshake_type in HandshakeType::iter() { + for cipher_suite in CipherSuite::iter() { + for kx_group in KXGroup::iter() { + for sig_type in SigType::iter() { + let crypto_config = CryptoConfig::new(cipher_suite, kx_group, sig_type); + let mut conn_pair = TlsConnPair::::new( + crypto_config, + handshake_type, + ConnectedBuffer::default(), + ) + .unwrap(); + + assert!(!conn_pair.handshake_completed()); + conn_pair.handshake().unwrap(); + assert!(conn_pair.handshake_completed()); + + assert!(conn_pair.negotiated_tls13()); + assert_eq!(cipher_suite, conn_pair.get_negotiated_cipher_suite()); + } } } } - )* } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{OpenSslHarness, RustlsHarness, S2NHarness, TlsBenchHarness}; - test_tls_bench_harnesses! { - s2n_tls: S2NHarness, - rustls: RustlsHarness, - openssl: OpenSslHarness, + fn test_transfer() { + // use a large buffer to test across TLS record boundaries + let mut buf = [0x56u8; 1000000]; + for cipher_suite in CipherSuite::iter() { + let crypto_config = + CryptoConfig::new(cipher_suite, KXGroup::default(), SigType::default()); + let mut conn_pair = TlsConnPair::::new( + crypto_config, + HandshakeType::default(), + ConnectedBuffer::default(), + ) + .unwrap(); + conn_pair.handshake().unwrap(); + conn_pair.round_trip_transfer(&mut buf).unwrap(); + } } } diff --git a/bindings/rust/bench/src/lib.rs b/bindings/rust/bench/src/lib.rs index b04e78585bd..4c9dad96754 100644 --- a/bindings/rust/bench/src/lib.rs +++ b/bindings/rust/bench/src/lib.rs @@ -5,29 +5,13 @@ pub mod harness; pub mod openssl; pub mod rustls; pub mod s2n_tls; + pub use crate::{ - harness::{CipherSuite, CryptoConfig, ECGroup, HandshakeType, Mode, TlsBenchHarness}, - openssl::OpenSslHarness, - rustls::RustlsHarness, - s2n_tls::S2NHarness, + harness::{ + get_cert_path, CipherSuite, CryptoConfig, HandshakeType, KXGroup, Mode, PemType, SigType, + TlsConnPair, TlsConnection, + }, + openssl::OpenSslConnection, + rustls::RustlsConnection, + s2n_tls::S2NConnection, }; - -const SERVER_KEY_PATH: &str = "certs/server-key.pem"; -const SERVER_CERT_CHAIN_PATH: &str = "certs/server-fullchain.pem"; -const CLIENT_KEY_PATH: &str = "certs/client-key.pem"; -const CLIENT_CERT_CHAIN_PATH: &str = "certs/client-fullchain.pem"; -const CA_CERT_PATH: &str = "certs/ca-cert.pem"; - -#[cfg(test)] -mod tests { - use std::path::Path; - - #[test] - fn cert_paths_valid() { - assert!(Path::new(crate::SERVER_KEY_PATH).exists()); - assert!(Path::new(crate::SERVER_CERT_CHAIN_PATH).exists()); - assert!(Path::new(crate::CLIENT_KEY_PATH).exists()); - assert!(Path::new(crate::CLIENT_CERT_CHAIN_PATH).exists()); - assert!(Path::new(crate::CA_CERT_PATH).exists()); - } -} diff --git a/bindings/rust/bench/src/openssl.rs b/bindings/rust/bench/src/openssl.rs index 8b9e48f7c64..0c30664e380 100644 --- a/bindings/rust/bench/src/openssl.rs +++ b/bindings/rust/bench/src/openssl.rs @@ -2,116 +2,142 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ + get_cert_path, harness::{ - CipherSuite, ConnectedBuffer, CryptoConfig, ECGroup, HandshakeType, Mode, TlsBenchHarness, + CipherSuite, ConnectedBuffer, CryptoConfig, HandshakeType, KXGroup, Mode, TlsConnection, }, - CA_CERT_PATH, CLIENT_CERT_CHAIN_PATH, CLIENT_KEY_PATH, SERVER_CERT_CHAIN_PATH, SERVER_KEY_PATH, + PemType::*, }; use openssl::ssl::{ - ErrorCode, Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslStream, - SslVerifyMode, SslVersion, + ErrorCode, Ssl, SslContext, SslFiletype, SslMethod, SslStream, SslVerifyMode, SslVersion, }; use std::{ error::Error, io::{Read, Write}, }; -pub struct OpenSslHarness { - client_conn: SslStream, - server_conn: SslStream, +pub struct OpenSslConnection { + connected_buffer: ConnectedBuffer, + connection: SslStream, } -impl OpenSslHarness { - fn common_config( - builder: &mut SslContextBuilder, - cipher_suite: &str, - ec_key: &str, - ) -> Result<(), Box> { - builder.set_min_proto_version(Some(SslVersion::TLS1_3))?; - builder.set_ciphersuites(cipher_suite)?; - builder.set_groups_list(ec_key)?; - Ok(()) +impl TlsConnection for OpenSslConnection { + type Config = SslContext; + + fn name() -> String { + let version_num = openssl::version::number() as u64; + let patch: u8 = (version_num >> 4) as u8; + let fix = (version_num >> 12) as u8; + let minor = (version_num >> 20) as u8; + let major = (version_num >> 28) as u8; + format!( + "openssl{}.{}.{}{}", + major, + minor, + fix, + (b'a' + patch - 1) as char + ) } - /// Process handshake for one connection, treating blocking errors as `Ok` - fn handshake_conn(&mut self, mode: Mode) -> Result<(), Box> { - let result = match mode { - Mode::Client => self.client_conn.connect(), - Mode::Server => self.server_conn.accept(), - }; - match result { - Ok(_) => Ok(()), - Err(err) => { - if err.code() != ErrorCode::WANT_READ { - Err(err.into()) - } else { - Ok(()) - } - } - } - } -} -impl TlsBenchHarness for OpenSslHarness { - fn new( + fn make_config( + mode: Mode, crypto_config: CryptoConfig, handshake_type: HandshakeType, - ) -> Result> { - let client_buf = ConnectedBuffer::new(); - let server_buf = client_buf.clone_inverse(); - + ) -> Result> { let cipher_suite = match crypto_config.cipher_suite { CipherSuite::AES_128_GCM_SHA256 => "TLS_AES_128_GCM_SHA256", CipherSuite::AES_256_GCM_SHA384 => "TLS_AES_256_GCM_SHA384", }; - let ec_key = match crypto_config.ec_group { - ECGroup::SECP256R1 => "P-256", - ECGroup::X25519 => "X25519", + let ec_key = match crypto_config.kx_group { + KXGroup::Secp256R1 => "P-256", + KXGroup::X25519 => "X25519", }; - let mut client_builder = SslContext::builder(SslMethod::tls_client())?; - client_builder.set_ca_file(CA_CERT_PATH)?; - Self::common_config(&mut client_builder, cipher_suite, ec_key)?; + let ssl_method = match mode { + Mode::Client => SslMethod::tls_client(), + Mode::Server => SslMethod::tls_server(), + }; - let mut server_builder = SslContext::builder(SslMethod::tls_server())?; - server_builder.set_certificate_chain_file(SERVER_CERT_CHAIN_PATH)?; - server_builder.set_private_key_file(SERVER_KEY_PATH, SslFiletype::PEM)?; - Self::common_config(&mut server_builder, cipher_suite, ec_key)?; + let mut builder = SslContext::builder(ssl_method)?; + builder.set_min_proto_version(Some(SslVersion::TLS1_3))?; + builder.set_ciphersuites(cipher_suite)?; + builder.set_groups_list(ec_key)?; - if handshake_type == HandshakeType::MutualAuth { - client_builder.set_certificate_chain_file(CLIENT_CERT_CHAIN_PATH)?; - client_builder.set_private_key_file(CLIENT_KEY_PATH, SslFiletype::PEM)?; - server_builder.set_ca_file(CA_CERT_PATH)?; - server_builder.set_verify(SslVerifyMode::FAIL_IF_NO_PEER_CERT | SslVerifyMode::PEER); + match mode { + Mode::Client => { + builder.set_ca_file(get_cert_path(CACert, crypto_config.sig_type))?; + builder.set_verify(SslVerifyMode::FAIL_IF_NO_PEER_CERT | SslVerifyMode::PEER); + + if handshake_type == HandshakeType::MutualAuth { + builder.set_certificate_chain_file(get_cert_path( + ClientCertChain, + crypto_config.sig_type, + ))?; + builder.set_private_key_file( + get_cert_path(ClientKey, crypto_config.sig_type), + SslFiletype::PEM, + )?; + } + } + Mode::Server => { + builder.set_certificate_chain_file(get_cert_path( + ServerCertChain, + crypto_config.sig_type, + ))?; + builder.set_private_key_file( + get_cert_path(ServerKey, crypto_config.sig_type), + SslFiletype::PEM, + )?; + + if handshake_type == HandshakeType::MutualAuth { + builder.set_ca_file(get_cert_path(CACert, crypto_config.sig_type))?; + builder.set_verify(SslVerifyMode::FAIL_IF_NO_PEER_CERT | SslVerifyMode::PEER); + } + } } - let client_config = client_builder.build(); - let server_config = server_builder.build(); - - let client_conn = SslStream::new(Ssl::new(&client_config)?, client_buf)?; - let server_conn = SslStream::new(Ssl::new(&server_config)?, server_buf)?; + Ok(builder.build()) + } + fn new_from_config( + config: &Self::Config, + connected_buffer: ConnectedBuffer, + ) -> Result> { + let connection = SslStream::new(Ssl::new(config)?, connected_buffer.clone())?; Ok(Self { - client_conn, - server_conn, + connected_buffer, + connection, }) } fn handshake(&mut self) -> Result<(), Box> { - for _ in 0..2 { - self.handshake_conn(Mode::Client)?; - self.handshake_conn(Mode::Server)?; + let result = if self.connection.ssl().is_server() { + self.connection.accept() + } else { + self.connection.connect() + }; + + // treat blocking (`ErrorCode::WANT_READ`) as `Ok`, expected during handshake + match result { + Ok(_) => Ok(()), + Err(err) => { + if err.code() != ErrorCode::WANT_READ { + Err(err.into()) + } else { + Ok(()) + } + } } - Ok(()) } fn handshake_completed(&self) -> bool { - self.client_conn.ssl().is_init_finished() && self.server_conn.ssl().is_init_finished() + self.connection.ssl().is_init_finished() } fn get_negotiated_cipher_suite(&self) -> CipherSuite { let cipher_suite = self - .client_conn + .connection .ssl() .current_cipher() .expect("Handshake not completed") @@ -124,39 +150,42 @@ impl TlsBenchHarness for OpenSslHarness { } fn negotiated_tls13(&self) -> bool { - self.client_conn + self.connection .ssl() .version2() // version() -> &str is deprecated, version2() returns an enum instead .expect("Handshake not completed") == SslVersion::TLS1_3 } - fn send(&mut self, sender: Mode, data: &[u8]) -> Result<(), Box> { - let send_conn = match sender { - Mode::Client => &mut self.client_conn, - Mode::Server => &mut self.server_conn, - }; - + fn send(&mut self, data: &[u8]) -> Result<(), Box> { let mut write_offset = 0; while write_offset < data.len() { - write_offset += send_conn.write(&data[write_offset..data.len()])?; - send_conn.flush()?; // make sure internal buffers don't fill up + write_offset += self.connection.write(&data[write_offset..data.len()])?; + self.connection.flush()?; // make sure internal buffers don't fill up } - Ok(()) } - fn recv(&mut self, receiver: Mode, data: &mut [u8]) -> Result<(), Box> { - let recv_conn = match receiver { - Mode::Client => &mut self.client_conn, - Mode::Server => &mut self.server_conn, - }; - + fn recv(&mut self, data: &mut [u8]) -> Result<(), Box> { + let data_len = data.len(); let mut read_offset = 0; while read_offset < data.len() { - read_offset += recv_conn.read(data)? + read_offset += self.connection.read(&mut data[read_offset..data_len])? } - Ok(()) } + + /// With OpenSSL's API, not possible after connection initialization: + /// In order to shrink buffers owned by the connection, config has to built + /// with `builder.set_mode(SslMode::RELEASE_BUFFERS);`, which tells the + /// connection to release buffers only when it's idle + fn shrink_connection_buffers(&mut self) {} + + fn shrink_connected_buffer(&mut self) { + self.connected_buffer.shrink(); + } + + fn connected_buffer(&self) -> &ConnectedBuffer { + &self.connected_buffer + } } diff --git a/bindings/rust/bench/src/rustls.rs b/bindings/rust/bench/src/rustls.rs index 450eeedf059..e16f2279dfe 100644 --- a/bindings/rust/bench/src/rustls.rs +++ b/bindings/rust/bench/src/rustls.rs @@ -3,19 +3,18 @@ use crate::{ harness::{ - read_to_bytes, CipherSuite, ConnectedBuffer, CryptoConfig, ECGroup, HandshakeType, Mode, - TlsBenchHarness, + read_to_bytes, CipherSuite, ConnectedBuffer, CryptoConfig, HandshakeType, KXGroup, Mode, + TlsConnection, }, - CA_CERT_PATH, CLIENT_CERT_CHAIN_PATH, CLIENT_KEY_PATH, SERVER_CERT_CHAIN_PATH, SERVER_KEY_PATH, + PemType::{self, *}, + SigType, }; use rustls::{ cipher_suite::{TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384}, kx_group::{SECP256R1, X25519}, server::AllowAnyAuthenticatedClient, version::TLS13, - Certificate, ClientConfig, ClientConnection, Connection, - Connection::{Client, Server}, - PrivateKey, + Certificate, ClientConfig, ClientConnection, Connection, PrivateKey, ProtocolVersion::TLSv1_3, RootCertStore, ServerConfig, ServerConnection, ServerName, }; @@ -26,37 +25,38 @@ use std::{ sync::Arc, }; -pub struct RustlsHarness { - client_buf: ConnectedBuffer, - server_buf: ConnectedBuffer, - client_conn: Connection, - server_conn: Connection, +pub struct RustlsConnection { + connected_buffer: ConnectedBuffer, + connection: Connection, } -impl RustlsHarness { - fn get_root_cert_store() -> Result> { +impl RustlsConnection { + fn get_root_cert_store(sig_type: SigType) -> Result> { let root_cert = - Certificate(certs(&mut BufReader::new(&*read_to_bytes(CA_CERT_PATH)))?.remove(0)); + Certificate(certs(&mut BufReader::new(&*read_to_bytes(CACert, sig_type)))?.remove(0)); let mut root_certs = RootCertStore::empty(); root_certs.add(&root_cert)?; Ok(root_certs) } - fn get_cert_chain(path: &str) -> Result, Box> { - let chain = certs(&mut BufReader::new(&*read_to_bytes(path)))?; + fn get_cert_chain( + pem_type: PemType, + sig_type: SigType, + ) -> Result, Box> { + let chain = certs(&mut BufReader::new(&*read_to_bytes(pem_type, sig_type)))?; Ok(chain .iter() .map(|bytes| Certificate(bytes.to_vec())) .collect()) } - fn get_key(path: &str) -> Result> { + fn get_key(pem_type: PemType, sig_type: SigType) -> Result> { Ok(PrivateKey( - pkcs8_private_keys(&mut BufReader::new(&*read_to_bytes(path)))?.remove(0), + pkcs8_private_keys(&mut BufReader::new(&*read_to_bytes(pem_type, sig_type)))?.remove(0), )) } - /// Treat `WouldBlock` as `Ok` for when blocking is expected + /// Treat `WouldBlock` as an `Ok` value for when blocking is expected fn ignore_block(res: Result) -> Result { match res { Ok(t) => Ok(t), @@ -68,85 +68,108 @@ impl RustlsHarness { } } -impl TlsBenchHarness for RustlsHarness { - fn new( +/// Clients and servers have different config types in Rustls, so wrap them in an enum +pub enum RustlsConfig { + Client(Arc), + Server(Arc), +} + +impl TlsConnection for RustlsConnection { + type Config = RustlsConfig; + + fn name() -> String { + "rustls".to_string() + } + + fn make_config( + mode: Mode, crypto_config: CryptoConfig, handshake_type: HandshakeType, - ) -> Result> { - let client_buf = ConnectedBuffer::new(); - let server_buf = client_buf.clone_inverse(); - + ) -> Result> { let cipher_suite = match crypto_config.cipher_suite { CipherSuite::AES_128_GCM_SHA256 => TLS13_AES_128_GCM_SHA256, CipherSuite::AES_256_GCM_SHA384 => TLS13_AES_256_GCM_SHA384, }; - let kx_group = match crypto_config.ec_group { - ECGroup::SECP256R1 => &SECP256R1, - ECGroup::X25519 => &X25519, + let kx_group = match crypto_config.kx_group { + KXGroup::Secp256R1 => &SECP256R1, + KXGroup::X25519 => &X25519, }; - let client_builder = ClientConfig::builder() - .with_cipher_suites(&[cipher_suite]) - .with_kx_groups(&[kx_group]) - .with_protocol_versions(&[&TLS13])? - .with_root_certificates(Self::get_root_cert_store()?); - - let server_builder = ServerConfig::builder() - .with_cipher_suites(&[cipher_suite]) - .with_kx_groups(&[kx_group]) - .with_protocol_versions(&[&TLS13])?; - - let (client_builder, server_builder) = match handshake_type { - HandshakeType::MutualAuth => ( - client_builder.with_client_auth_cert( - Self::get_cert_chain(CLIENT_CERT_CHAIN_PATH)?, - Self::get_key(CLIENT_KEY_PATH)?, - )?, - server_builder.with_client_cert_verifier(Arc::new( - AllowAnyAuthenticatedClient::new(Self::get_root_cert_store()?), - )), - ), - HandshakeType::ServerAuth => ( - client_builder.with_no_client_auth(), - server_builder.with_no_client_auth(), - ), - }; - - let client_config = Arc::new(client_builder); - let server_config = Arc::new(server_builder.with_single_cert( - Self::get_cert_chain(SERVER_CERT_CHAIN_PATH)?, - Self::get_key(SERVER_KEY_PATH)?, - )?); + match mode { + Mode::Client => { + let builder = ClientConfig::builder() + .with_cipher_suites(&[cipher_suite]) + .with_kx_groups(&[kx_group]) + .with_protocol_versions(&[&TLS13])? + .with_root_certificates(Self::get_root_cert_store(crypto_config.sig_type)?); + + let config = match handshake_type { + HandshakeType::ServerAuth => builder.with_no_client_auth(), + HandshakeType::MutualAuth => builder.with_client_auth_cert( + Self::get_cert_chain(ClientCertChain, crypto_config.sig_type)?, + Self::get_key(ClientKey, crypto_config.sig_type)?, + )?, + }; + + Ok(RustlsConfig::Client(Arc::new(config))) + } + Mode::Server => { + let builder = ServerConfig::builder() + .with_cipher_suites(&[cipher_suite]) + .with_kx_groups(&[kx_group]) + .with_protocol_versions(&[&TLS13])?; + + let builder = match handshake_type { + HandshakeType::ServerAuth => builder.with_no_client_auth(), + HandshakeType::MutualAuth => builder.with_client_cert_verifier(Arc::new( + AllowAnyAuthenticatedClient::new(Self::get_root_cert_store( + crypto_config.sig_type, + )?), + )), + }; + + let config = builder.with_single_cert( + Self::get_cert_chain(ServerCertChain, crypto_config.sig_type)?, + Self::get_key(ServerKey, crypto_config.sig_type)?, + )?; + + Ok(RustlsConfig::Server(Arc::new(config))) + } + } + } - let client_conn = Client(ClientConnection::new( - client_config, - ServerName::try_from("localhost")?, - )?); - let server_conn = Server(ServerConnection::new(server_config)?); + fn new_from_config( + config: &Self::Config, + connected_buffer: ConnectedBuffer, + ) -> Result> { + let connection = match config { + RustlsConfig::Client(config) => Connection::Client(ClientConnection::new( + config.clone(), + ServerName::try_from("localhost")?, + )?), + RustlsConfig::Server(config) => { + Connection::Server(ServerConnection::new(config.clone())?) + } + }; Ok(Self { - client_buf, - server_buf, - client_conn, - server_conn, + connected_buffer, + connection, }) } fn handshake(&mut self) -> Result<(), Box> { - for _ in 0..2 { - Self::ignore_block(self.client_conn.complete_io(&mut self.client_buf))?; - Self::ignore_block(self.server_conn.complete_io(&mut self.server_buf))?; - } + Self::ignore_block(self.connection.complete_io(&mut self.connected_buffer))?; Ok(()) } fn handshake_completed(&self) -> bool { - !self.client_conn.is_handshaking() && !self.server_conn.is_handshaking() + !self.connection.is_handshaking() } fn get_negotiated_cipher_suite(&self) -> CipherSuite { - match self.client_conn.negotiated_cipher_suite().unwrap().suite() { + match self.connection.negotiated_cipher_suite().unwrap().suite() { rustls::CipherSuite::TLS13_AES_128_GCM_SHA256 => CipherSuite::AES_128_GCM_SHA256, rustls::CipherSuite::TLS13_AES_256_GCM_SHA384 => CipherSuite::AES_256_GCM_SHA384, _ => panic!("Unknown cipher suite"), @@ -154,42 +177,48 @@ impl TlsBenchHarness for RustlsHarness { } fn negotiated_tls13(&self) -> bool { - self.client_conn + self.connection .protocol_version() .expect("Handshake not completed") == TLSv1_3 } - fn send(&mut self, sender: Mode, data: &[u8]) -> Result<(), Box> { - let (send_conn, send_buf) = match sender { - Mode::Client => (&mut self.client_conn, &mut self.client_buf), - Mode::Server => (&mut self.server_conn, &mut self.server_buf), - }; - + fn send(&mut self, data: &[u8]) -> Result<(), Box> { let mut write_offset = 0; while write_offset < data.len() { - write_offset += send_conn.writer().write(&data[write_offset..data.len()])?; - send_conn.writer().flush()?; - send_conn.complete_io(send_buf)?; + write_offset += self + .connection + .writer() + .write(&data[write_offset..data.len()])?; + self.connection.writer().flush()?; + self.connection.complete_io(&mut self.connected_buffer)?; } - Ok(()) } - fn recv(&mut self, receiver: Mode, data: &mut [u8]) -> Result<(), Box> { - let (recv_conn, recv_buf) = match receiver { - Mode::Client => (&mut self.client_conn, &mut self.client_buf), - Mode::Server => (&mut self.server_conn, &mut self.server_buf), - }; - + fn recv(&mut self, data: &mut [u8]) -> Result<(), Box> { let data_len = data.len(); let mut read_offset = 0; while read_offset < data.len() { - recv_conn.complete_io(recv_buf)?; - read_offset += - Self::ignore_block(recv_conn.reader().read(&mut data[read_offset..data_len]))?; + self.connection.complete_io(&mut self.connected_buffer)?; + read_offset += Self::ignore_block( + self.connection + .reader() + .read(&mut data[read_offset..data_len]), + )?; } - Ok(()) } + + fn shrink_connection_buffers(&mut self) { + self.connection.set_buffer_limit(Some(1)); + } + + fn shrink_connected_buffer(&mut self) { + self.connected_buffer.shrink(); + } + + fn connected_buffer(&self) -> &ConnectedBuffer { + &self.connected_buffer + } } diff --git a/bindings/rust/bench/src/s2n_tls.rs b/bindings/rust/bench/src/s2n_tls.rs index d2a297f6d5e..caa30d92d8c 100644 --- a/bindings/rust/bench/src/s2n_tls.rs +++ b/bindings/rust/bench/src/s2n_tls.rs @@ -3,41 +3,26 @@ use crate::{ harness::{ - read_to_bytes, CipherSuite, CryptoConfig, ECGroup, HandshakeType, Mode, TlsBenchHarness, + read_to_bytes, CipherSuite, ConnectedBuffer, CryptoConfig, HandshakeType, KXGroup, Mode, + TlsConnection, }, - CA_CERT_PATH, CLIENT_CERT_CHAIN_PATH, CLIENT_KEY_PATH, SERVER_CERT_CHAIN_PATH, SERVER_KEY_PATH, + PemType::*, }; use s2n_tls::{ callbacks::VerifyHostNameCallback, - config::{Builder, Config}, + config::Builder, connection::Connection, enums::{Blinding, ClientAuthType, Version}, security::Policy, }; use std::{ - cell::UnsafeCell, - collections::VecDeque, error::Error, ffi::c_void, - io::{Read, Write}, + io::{ErrorKind, Read, Write}, os::raw::c_int, pin::Pin, - task::Poll::Ready, }; -pub struct S2NHarness { - // UnsafeCell is needed b/c client and server share *mut to IO buffers - // Pin> is to ensure long-term *mut to IO buffers remain valid - client_to_server_buf: Pin>>>, - server_to_client_buf: Pin>>>, - client_config: Config, - server_config: Config, - client_conn: Connection, - server_conn: Connection, - client_handshake_completed: bool, - server_handshake_completed: bool, -} - /// Custom callback for verifying hostnames. Rustls requires checking hostnames, /// so this is to make a fair comparison struct HostNameHandler<'a> { @@ -49,40 +34,69 @@ impl VerifyHostNameCallback for HostNameHandler<'_> { } } -impl S2NHarness { +/// s2n-tls has mode-independent configs, so this struct wraps the config with the mode +pub struct S2NConfig { + mode: Mode, + config: s2n_tls::config::Config, +} + +pub struct S2NConnection { + // Pin> is to ensure long-term *mut to IO buffers remains valid + connected_buffer: Pin>, + connection: Connection, + handshake_completed: bool, +} + +impl S2NConnection { /// Unsafe callback for custom IO C API /// /// s2n-tls IO is usually used with file descriptors to a TCP socket, but we /// reduce overhead and outside noise with a local buffer for benchmarking unsafe extern "C" fn send_cb(context: *mut c_void, data: *const u8, len: u32) -> c_int { - let context = &mut *(context as *mut VecDeque); + let context = &mut *(context as *mut ConnectedBuffer); let data = core::slice::from_raw_parts(data, len as _); context.write(data).unwrap() as _ } /// Unsafe callback for custom IO C API unsafe extern "C" fn recv_cb(context: *mut c_void, data: *mut u8, len: u32) -> c_int { - let context = &mut *(context as *mut VecDeque); + let context = &mut *(context as *mut ConnectedBuffer); let data = core::slice::from_raw_parts_mut(data, len as _); context.flush().unwrap(); - let len = context.read(data).unwrap(); - if len == 0 { - errno::set_errno(errno::Errno(libc::EWOULDBLOCK)); - -1 - } else { - len as _ + match context.read(data) { + Err(err) => { + // s2n-tls requires the callback to set errno if blocking happens + if let ErrorKind::WouldBlock = err.kind() { + errno::set_errno(errno::Errno(libc::EWOULDBLOCK)); + -1 + } else { + panic!("{err:?}"); + } + } + Ok(len) => len as _, } } +} + +impl TlsConnection for S2NConnection { + type Config = S2NConfig; + + fn name() -> String { + "s2n-tls".to_string() + } - fn create_common_config_builder( + fn make_config( + mode: Mode, crypto_config: CryptoConfig, handshake_type: HandshakeType, - ) -> Result> { - let security_policy = match (crypto_config.cipher_suite, crypto_config.ec_group) { - (CipherSuite::AES_128_GCM_SHA256, ECGroup::SECP256R1) => "20230317", - (CipherSuite::AES_256_GCM_SHA384, ECGroup::SECP256R1) => "20190802", - (CipherSuite::AES_128_GCM_SHA256, ECGroup::X25519) => "default_tls13", - (CipherSuite::AES_256_GCM_SHA384, ECGroup::X25519) => "20190801", + ) -> Result> { + // these security policies negotiate the given cipher suite and key + // exchange group as their top choice + let security_policy = match (crypto_config.cipher_suite, crypto_config.kx_group) { + (CipherSuite::AES_128_GCM_SHA256, KXGroup::Secp256R1) => "20230317", + (CipherSuite::AES_256_GCM_SHA384, KXGroup::Secp256R1) => "20190802", + (CipherSuite::AES_128_GCM_SHA256, KXGroup::X25519) => "default_tls13", + (CipherSuite::AES_256_GCM_SHA384, KXGroup::X25519) => "20190801", }; let mut builder = Builder::new(); @@ -94,141 +108,90 @@ impl S2NHarness { HandshakeType::MutualAuth => ClientAuthType::Required, })?; - Ok(builder) - } - - fn create_client_config( - crypto_config: CryptoConfig, - handshake_type: HandshakeType, - ) -> Result> { - let mut builder = Self::create_common_config_builder(crypto_config, handshake_type)?; - builder - .trust_pem(read_to_bytes(CA_CERT_PATH).as_slice())? - .set_verify_host_callback(HostNameHandler { - expected_server_name: "localhost", - })?; - - if handshake_type == HandshakeType::MutualAuth { - builder.load_pem( - read_to_bytes(CLIENT_CERT_CHAIN_PATH).as_slice(), - read_to_bytes(CLIENT_KEY_PATH).as_slice(), - )?; + match mode { + Mode::Client => { + builder + .trust_pem(read_to_bytes(CACert, crypto_config.sig_type).as_slice())? + .set_verify_host_callback(HostNameHandler { + expected_server_name: "localhost", + })?; + + if handshake_type == HandshakeType::MutualAuth { + builder.load_pem( + read_to_bytes(ClientCertChain, crypto_config.sig_type).as_slice(), + read_to_bytes(ClientKey, crypto_config.sig_type).as_slice(), + )?; + } + } + Mode::Server => { + builder.load_pem( + read_to_bytes(ServerCertChain, crypto_config.sig_type).as_slice(), + read_to_bytes(ServerKey, crypto_config.sig_type).as_slice(), + )?; + + if handshake_type == HandshakeType::MutualAuth { + builder + .trust_pem(read_to_bytes(CACert, crypto_config.sig_type).as_slice())? + .set_verify_host_callback(HostNameHandler { + expected_server_name: "localhost", + })?; + } + } } - Ok(builder.build()?) + Ok(S2NConfig { + mode, + config: builder.build()?, + }) } - fn create_server_config( - crypto_config: CryptoConfig, - handshake_type: HandshakeType, - ) -> Result> { - let mut builder = Self::create_common_config_builder(crypto_config, handshake_type)?; - builder.load_pem( - read_to_bytes(SERVER_CERT_CHAIN_PATH).as_slice(), - read_to_bytes(SERVER_KEY_PATH).as_slice(), - )?; - - if handshake_type == HandshakeType::MutualAuth { - builder - .trust_pem(read_to_bytes(CA_CERT_PATH).as_slice())? - .set_verify_host_callback(HostNameHandler { - expected_server_name: "localhost", - })?; - } - - Ok(builder.build()?) - } - - /// Set up connections with config and custom IO - fn init_conn(&mut self, mode: Mode) -> Result<(), Box> { - let client_to_server_ptr = self.client_to_server_buf.get() as *mut c_void; - let server_to_client_ptr = self.server_to_client_buf.get() as *mut c_void; - let (read_ptr, write_ptr, config, conn) = match mode { - Mode::Client => ( - server_to_client_ptr, - client_to_server_ptr, - &self.client_config, - &mut self.client_conn, - ), - Mode::Server => ( - client_to_server_ptr, - server_to_client_ptr, - &self.server_config, - &mut self.server_conn, - ), + fn new_from_config( + config: &Self::Config, + connected_buffer: ConnectedBuffer, + ) -> Result> { + let mode = match config.mode { + Mode::Client => s2n_tls::enums::Mode::Client, + Mode::Server => s2n_tls::enums::Mode::Server, }; - conn.set_blinding(Blinding::SelfService)? - .set_config(config.clone())? + let mut connected_buffer = Box::pin(connected_buffer); + + let mut connection = Connection::new(mode); + connection + .set_blinding(Blinding::SelfService)? + .set_config(config.config.clone())? .set_send_callback(Some(Self::send_cb))? .set_receive_callback(Some(Self::recv_cb))?; unsafe { - conn.set_send_context(write_ptr)? - .set_receive_context(read_ptr)?; - } - - Ok(()) - } - - /// Handshake step for one connection - fn handshake_conn(&mut self, mode: Mode) -> Result<(), Box> { - let (conn, handshake_completed) = match mode { - Mode::Client => (&mut self.client_conn, &mut self.client_handshake_completed), - Mode::Server => (&mut self.server_conn, &mut self.server_handshake_completed), - }; - - if let Ready(res) = conn.poll_negotiate() { - res?; - *handshake_completed = true; - } else { - *handshake_completed = false; + connection + .set_send_context(&mut *connected_buffer as *mut ConnectedBuffer as *mut c_void)? + .set_receive_context( + &mut *connected_buffer as *mut ConnectedBuffer as *mut c_void, + )?; } - Ok(()) - } -} - -impl TlsBenchHarness for S2NHarness { - fn new( - crypto_config: CryptoConfig, - handshake_type: HandshakeType, - ) -> Result> { - let client_to_server_buf = Box::pin(UnsafeCell::new(VecDeque::new())); - let server_to_client_buf = Box::pin(UnsafeCell::new(VecDeque::new())); - - let client_config = Self::create_client_config(crypto_config, handshake_type)?; - let server_config = Self::create_server_config(crypto_config, handshake_type)?; - let mut harness = Self { - client_to_server_buf, - server_to_client_buf, - client_config, - server_config, - client_conn: Connection::new_client(), - server_conn: Connection::new_server(), - client_handshake_completed: false, - server_handshake_completed: false, - }; - - harness.init_conn(Mode::Client)?; - harness.init_conn(Mode::Server)?; - - Ok(harness) + Ok(Self { + connected_buffer, + connection, + handshake_completed: false, + }) } fn handshake(&mut self) -> Result<(), Box> { - for _ in 0..2 { - self.handshake_conn(Mode::Client)?; - self.handshake_conn(Mode::Server)?; - } + self.handshake_completed = self + .connection + .poll_negotiate() + .map(|res| res.unwrap()) // unwrap `Err` if present + .is_ready(); Ok(()) } fn handshake_completed(&self) -> bool { - self.client_handshake_completed && self.server_handshake_completed + self.handshake_completed } fn get_negotiated_cipher_suite(&self) -> CipherSuite { - match self.client_conn.cipher_suite().unwrap() { + match self.connection.cipher_suite().unwrap() { "TLS_AES_128_GCM_SHA256" => CipherSuite::AES_128_GCM_SHA256, "TLS_AES_256_GCM_SHA384" => CipherSuite::AES_256_GCM_SHA384, _ => panic!("Unknown cipher suite"), @@ -236,28 +199,29 @@ impl TlsBenchHarness for S2NHarness { } fn negotiated_tls13(&self) -> bool { - self.client_conn.actual_protocol_version().unwrap() == Version::TLS13 + self.connection.actual_protocol_version().unwrap() == Version::TLS13 } - fn send(&mut self, sender: Mode, data: &[u8]) -> Result<(), Box> { - let send_conn = match sender { - Mode::Client => &mut self.client_conn, - Mode::Server => &mut self.server_conn, - }; - - assert!(send_conn.poll_send(data).is_ready()); - assert!(send_conn.poll_flush().is_ready()); + fn send(&mut self, data: &[u8]) -> Result<(), Box> { + assert!(self.connection.poll_send(data).is_ready()); + assert!(self.connection.poll_flush().is_ready()); + Ok(()) + } + fn recv(&mut self, data: &mut [u8]) -> Result<(), Box> { + assert!(self.connection.poll_recv(data).is_ready()); Ok(()) } - fn recv(&mut self, receiver: Mode, data: &mut [u8]) -> Result<(), Box> { - let recv_conn = match receiver { - Mode::Client => &mut self.client_conn, - Mode::Server => &mut self.server_conn, - }; + fn shrink_connection_buffers(&mut self) { + self.connection.release_buffers().unwrap(); + } - assert!(recv_conn.poll_recv(data).is_ready()); - Ok(()) + fn shrink_connected_buffer(&mut self) { + self.connected_buffer.shrink(); + } + + fn connected_buffer(&self) -> &ConnectedBuffer { + &self.connected_buffer } } diff --git a/bindings/rust/s2n-tls-sys/templates/Cargo.template b/bindings/rust/s2n-tls-sys/templates/Cargo.template index b9b841fac2a..55f8cfef955 100644 --- a/bindings/rust/s2n-tls-sys/templates/Cargo.template +++ b/bindings/rust/s2n-tls-sys/templates/Cargo.template @@ -1,7 +1,7 @@ [package] name = "s2n-tls-sys" description = "A C99 implementation of the TLS/SSL protocols" -version = "0.0.34" +version = "0.0.35" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" diff --git a/bindings/rust/s2n-tls-tokio/Cargo.toml b/bindings/rust/s2n-tls-tokio/Cargo.toml index acb6a08b09b..145e6a05de6 100644 --- a/bindings/rust/s2n-tls-tokio/Cargo.toml +++ b/bindings/rust/s2n-tls-tokio/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "s2n-tls-tokio" description = "An implementation of TLS streams for Tokio built on top of s2n-tls" -version = "0.0.34" +version = "0.0.35" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -15,7 +15,7 @@ default = [] errno = { version = "0.3" } libc = { version = "0.2" } pin-project-lite = { version = "0.2" } -s2n-tls = { version = "=0.0.34", path = "../s2n-tls" } +s2n-tls = { version = "=0.0.35", path = "../s2n-tls" } tokio = { version = "1", features = ["net", "time"] } [dev-dependencies] diff --git a/bindings/rust/s2n-tls/Cargo.toml b/bindings/rust/s2n-tls/Cargo.toml index 9bd3487beb6..45bd4f574c9 100644 --- a/bindings/rust/s2n-tls/Cargo.toml +++ b/bindings/rust/s2n-tls/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "s2n-tls" description = "A C99 implementation of the TLS/SSL protocols" -version = "0.0.34" +version = "0.0.35" authors = ["AWS s2n"] edition = "2021" rust-version = "1.63.0" @@ -19,7 +19,7 @@ testing = ["bytes"] bytes = { version = "1", optional = true } errno = { version = "0.3" } libc = "0.2" -s2n-tls-sys = { version = "=0.0.34", path = "../s2n-tls-sys", features = ["internal"] } +s2n-tls-sys = { version = "=0.0.35", path = "../s2n-tls-sys", features = ["internal"] } pin-project-lite = "0.2" hex = "0.4" diff --git a/codebuild/spec/buildspec_32bit_cross_compile.yml b/codebuild/spec/buildspec_32bit_cross_compile.yml index 8886e38c49a..6fbf04e2f79 100644 --- a/codebuild/spec/buildspec_32bit_cross_compile.yml +++ b/codebuild/spec/buildspec_32bit_cross_compile.yml @@ -22,4 +22,4 @@ phases: post_build: on-failure: ABORT commands: - - CTEST_PARALLEL_LEVEL=$(nproc) make -C build test + - CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=$(nproc) make -C build test diff --git a/docs/BUILD.md b/docs/BUILD.md new file mode 100644 index 00000000000..e2e7842ef54 --- /dev/null +++ b/docs/BUILD.md @@ -0,0 +1,263 @@ +# Building s2n-tls + +Obtain s2n-tls by cloning the Github repo locally: +```shell +git clone https://github.com/aws/s2n-tls.git +cd s2n-tls +``` + +## Building + +s2n-tls can be built as follows: + +
+Ubuntu + +```shell +# install build dependencies +sudo apt update +sudo apt install cmake ninja-build + +# install a libcrypto +sudo apt install libssl-dev + +# build s2n-tls +cmake . -Bbuild -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=./s2n-tls-install +ninja -C build -j $(nproc) +CTEST_PARALLEL_LEVEL=$(nproc) ninja -C build test +ninja -C build install +``` +
+ +
+MacOS + +```bash +# install build dependencies +brew install cmake ninja + +# install a libcrypto +brew install openssl@3 + +# build s2n-tls +cmake . -Bbuild -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH=$(dirname $(dirname $(brew list openssl@3|grep libcrypto.dylib))) \ + -DCMAKE_INSTALL_PREFIX=./s2n-tls-install +ninja -C build -j $(sysctl -n hw.ncpu) +CTEST_PARALLEL_LEVEL=$(sysctl -n hw.ncpu) ninja -C build test +ninja -C build install +``` +
+ +
+AL2 + +```bash +# install build dependencies +sudo yum groupinstall "Development Tools" +sudo yum install cmake3 ninja-build + +# install a libcrypto +sudo yum install openssl-devel + +# build s2n-tls +cmake . -Bbuild -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=./s2n-tls-install \ + -DCMAKE_EXE_LINKER_FLAGS="-lcrypto -lz" +ninja -C build -j $(nproc) +CTEST_PARALLEL_LEVEL=$(nproc) ninja -C build test +ninja -C build install +``` +
+ +s2n-tls can be configured with the following CMake options. Each option can be set by passing a `-D