mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-06 04:40:55 -08:00
Add benchmarking for cryptographic primitives and protocol performance
This commit introduces two kinds of benchmarks: 1. Cryptographic Primitives. Measures the performance of all available implementations of cryptographic algorithms using traditional benchmarking. Uses criterion. 2. Protocol Runs. Measures the time each step in the protocol takes. Measured using a tracing-based approach. The benchmarks are run on CI and an interactive visual overview is written to the gh-pages branch. If a benchmark takes more than twice the time than the reference commit (for PR: the main branch), the action fails.
This commit is contained in:
106
.github/workflows/bench-primitives.yml
vendored
Normal file
106
.github/workflows/bench-primitives.yml
vendored
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
name: rosenpass-ciphers - primitives - benchmark
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prim-benchmark:
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
system: ["x86_64-linux", "i686-linux"]
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Install nix
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v27 # A popular action for installing Nix
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Set up environment
|
||||||
|
|
||||||
|
- name: 🛠️ Config Linux x64
|
||||||
|
run: echo "RUST_TARGET_FLAG=--target=x86_64-unknown-linux-gnu" > $GITHUB_ENV
|
||||||
|
if: ${{ matrix.system == 'x86_64-linux' }}
|
||||||
|
|
||||||
|
- name: 🛠️ Config Linux x86
|
||||||
|
run: echo "RUST_TARGET_FLAG=--target=i686-unknown-linux-gnu" > $GITHUB_ENV
|
||||||
|
if: ${{ matrix.system == 'i686-linux' }}
|
||||||
|
|
||||||
|
- name: 🛠️ Prepare Benchmark Path
|
||||||
|
env:
|
||||||
|
EVENT_NAME: ${{ github.event_name }}
|
||||||
|
BRANCH_NAME: ${{ github.ref_name }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
run: |
|
||||||
|
case "$EVENT_NAME" in
|
||||||
|
"push")
|
||||||
|
echo "BENCH_PATH=branch/$BRANCH_NAME" >> $GITHUB_ENV
|
||||||
|
;;
|
||||||
|
"pull_request")
|
||||||
|
echo "BENCH_PATH=pull/$PR_NUMBER" >> $GITHUB_ENV
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "don't know benchmark path for event of type $EVENT_NAME, aborting"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Benchmarks ...
|
||||||
|
|
||||||
|
- name: 🏃🏻♀️ Benchmarks (using Nix as shell)
|
||||||
|
working-directory: ciphers
|
||||||
|
run: nix develop ".#devShells.${{ matrix.system }}.benchmark" --command cargo bench -F bench --bench primitives --verbose $RUST_TARGET_FLAG -- --output-format bencher | tee ../bench-primitives.txt
|
||||||
|
|
||||||
|
- name: Extract benchmarks
|
||||||
|
uses: cryspen/benchmark-data-extract-transform@v2
|
||||||
|
with:
|
||||||
|
name: rosenpass-ciphers primitives benchmarks
|
||||||
|
tool: "cargo"
|
||||||
|
os: ${{ matrix.system }}
|
||||||
|
output-file-path: bench-primitives.txt
|
||||||
|
data-out-path: bench-primitives.json
|
||||||
|
|
||||||
|
- name: Upload benchmarks
|
||||||
|
uses: cryspen/benchmark-upload-and-plot-action@v3
|
||||||
|
with:
|
||||||
|
name: Crypto Primitives Benchmarks
|
||||||
|
group-by: "os,primitive,algorithm"
|
||||||
|
schema: "os,primitive,algorithm,implementation,operation,length"
|
||||||
|
input-data-path: bench-primitives.json
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# NOTE: pushes to current repository
|
||||||
|
gh-repository: github.com/${{ github.repository }}
|
||||||
|
# use the default (gh-pages) for the demo
|
||||||
|
#gh-pages-branch: benchmarks
|
||||||
|
auto-push: true
|
||||||
|
fail-on-alert: true
|
||||||
|
|
||||||
|
ciphers-primitives-bench-status:
|
||||||
|
if: ${{ always() }}
|
||||||
|
needs: [prim-benchmark]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Successful
|
||||||
|
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||||
|
run: exit 0
|
||||||
|
- name: Failing
|
||||||
|
if: ${{ (contains(needs.*.result, 'failure')) }}
|
||||||
|
run: exit 1
|
||||||
96
.github/workflows/bench-protocol.yml
vendored
Normal file
96
.github/workflows/bench-protocol.yml
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
name: rosenpass - protocol - benchmark
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
proto-benchmark:
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
system: ["x86_64-linux", "i686-linux"]
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Install nix
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v27 # A popular action for installing Nix
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Set up environment
|
||||||
|
|
||||||
|
- name: 🛠️ Config Linux x64
|
||||||
|
run: echo "RUST_TARGET_FLAG=--target=x86_64-unknown-linux-gnu" > $GITHUB_ENV
|
||||||
|
if: ${{ matrix.system == 'x86_64-linux' }}
|
||||||
|
|
||||||
|
- name: 🛠️ Config Linux x86
|
||||||
|
run: echo "RUST_TARGET_FLAG=--target=i686-unknown-linux-gnu" > $GITHUB_ENV
|
||||||
|
if: ${{ matrix.system == 'i686-linux' }}
|
||||||
|
|
||||||
|
- name: 🛠️ Prepare Benchmark Path
|
||||||
|
env:
|
||||||
|
EVENT_NAME: ${{ github.event_name }}
|
||||||
|
BRANCH_NAME: ${{ github.ref_name }}
|
||||||
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
|
run: |
|
||||||
|
case "$EVENT_NAME" in
|
||||||
|
"push")
|
||||||
|
echo "BENCH_PATH=branch/$BRANCH_NAME" >> $GITHUB_ENV
|
||||||
|
;;
|
||||||
|
"pull_request")
|
||||||
|
echo "BENCH_PATH=pull/$PR_NUMBER" >> $GITHUB_ENV
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "don't know benchmark path for event of type $EVENT_NAME, aborting"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Benchmarks ...
|
||||||
|
|
||||||
|
- name: 🏃🏻♀️ Benchmarks
|
||||||
|
run: nix develop ".#devShells.${{ matrix.system }}.benchmark" --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose $RUST_TARGET_FLAG >bench-protocol.json
|
||||||
|
|
||||||
|
- name: Upload benchmarks
|
||||||
|
uses: cryspen/benchmark-upload-and-plot-action@v3
|
||||||
|
with:
|
||||||
|
name: Protocol Benchmarks
|
||||||
|
group-by: "os,arch,protocol version,run time"
|
||||||
|
schema: "os,arch,protocol version,run time,name"
|
||||||
|
input-data-path: bench-protocol.json
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# NOTE: pushes to current repository
|
||||||
|
gh-repository: github.com/${{ github.repository }}
|
||||||
|
# use the default (gh-pages) for the demo
|
||||||
|
#gh-pages-branch: benchmarks
|
||||||
|
auto-push: true
|
||||||
|
fail-on-alert: true
|
||||||
|
|
||||||
|
ciphers-protocol-bench-status:
|
||||||
|
if: ${{ always() }}
|
||||||
|
needs: [proto-benchmark]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Successful
|
||||||
|
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
||||||
|
run: exit 0
|
||||||
|
- name: Failing
|
||||||
|
if: ${{ (contains(needs.*.result, 'failure')) }}
|
||||||
|
run: exit 1
|
||||||
29
Cargo.lock
generated
29
Cargo.lock
generated
@@ -1269,7 +1269,7 @@ version = "0.0.3-pre"
|
|||||||
source = "git+https://github.com/cryspen/libcrux.git?rev=10ce653e9476#10ce653e94761352b657b6cecdcc0c85675813df"
|
source = "git+https://github.com/cryspen/libcrux.git?rev=10ce653e9476#10ce653e94761352b657b6cecdcc0c85675813df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libcrux-hacl-rs",
|
"libcrux-hacl-rs",
|
||||||
"libcrux-macros",
|
"libcrux-macros 0.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1279,7 +1279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "78d522fb626847390ea4b776c7eca179ecec363c6c4730b61b0c0feb797b8d92"
|
checksum = "78d522fb626847390ea4b776c7eca179ecec363c6c4730b61b0c0feb797b8d92"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libcrux-hacl-rs",
|
"libcrux-hacl-rs",
|
||||||
"libcrux-macros",
|
"libcrux-macros 0.0.2",
|
||||||
"libcrux-poly1305",
|
"libcrux-poly1305",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1299,7 +1299,7 @@ version = "0.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8bba0885296a72555a5d77056c39cc9b04edd9ab1afa3025ef3dbd96220705c"
|
checksum = "f8bba0885296a72555a5d77056c39cc9b04edd9ab1afa3025ef3dbd96220705c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libcrux-macros",
|
"libcrux-macros 0.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1321,6 +1321,15 @@ dependencies = [
|
|||||||
"syn 2.0.98",
|
"syn 2.0.98",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libcrux-macros"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "git+https://github.com/cryspen/libcrux.git?rev=0ab6d2dd9c1f#0ab6d2dd9c1f39c82b1125a566d6befb38feea28"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.98",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libcrux-ml-kem"
|
name = "libcrux-ml-kem"
|
||||||
version = "0.0.2-beta.3"
|
version = "0.0.2-beta.3"
|
||||||
@@ -1350,7 +1359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "80143d78ae14ab51ceb2c8a9514fb60af6645d42a9c951bc511792c19c974fca"
|
checksum = "80143d78ae14ab51ceb2c8a9514fb60af6645d42a9c951bc511792c19c974fca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libcrux-hacl-rs",
|
"libcrux-hacl-rs",
|
||||||
"libcrux-macros",
|
"libcrux-macros 0.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1364,6 +1373,14 @@ dependencies = [
|
|||||||
"libcrux-platform",
|
"libcrux-platform",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libcrux-test-utils"
|
||||||
|
version = "0.0.2"
|
||||||
|
source = "git+https://github.com/cryspen/libcrux.git?rev=0ab6d2dd9c1f#0ab6d2dd9c1f39c82b1125a566d6befb38feea28"
|
||||||
|
dependencies = [
|
||||||
|
"libcrux-macros 0.0.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
@@ -2024,6 +2041,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"home",
|
"home",
|
||||||
|
"libcrux-test-utils",
|
||||||
"log",
|
"log",
|
||||||
"memoffset 0.9.1",
|
"memoffset 0.9.1",
|
||||||
"mio",
|
"mio",
|
||||||
@@ -2070,6 +2088,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"blake2",
|
"blake2",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
|
"criterion",
|
||||||
"libcrux",
|
"libcrux",
|
||||||
"libcrux-blake2",
|
"libcrux-blake2",
|
||||||
"libcrux-chacha20poly1305",
|
"libcrux-chacha20poly1305",
|
||||||
@@ -2153,6 +2172,8 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64ct",
|
"base64ct",
|
||||||
|
"lazy_static",
|
||||||
|
"libcrux-test-utils",
|
||||||
"mio",
|
"mio",
|
||||||
"rustix",
|
"rustix",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
|
|||||||
@@ -73,12 +73,14 @@ libcrux = { version = "0.0.2-pre.2" }
|
|||||||
libcrux-chacha20poly1305 = { version = "0.0.2-beta.3" }
|
libcrux-chacha20poly1305 = { version = "0.0.2-beta.3" }
|
||||||
libcrux-ml-kem = { version = "0.0.2-beta.3" }
|
libcrux-ml-kem = { version = "0.0.2-beta.3" }
|
||||||
libcrux-blake2 = { git = "https://github.com/cryspen/libcrux.git", rev = "10ce653e9476" }
|
libcrux-blake2 = { git = "https://github.com/cryspen/libcrux.git", rev = "10ce653e9476" }
|
||||||
|
libcrux-test-utils = { git = "https://github.com/cryspen/libcrux.git", rev = "0ab6d2dd9c1f" }
|
||||||
hex-literal = { version = "0.4.1" }
|
hex-literal = { version = "0.4.1" }
|
||||||
hex = { version = "0.4.3" }
|
hex = { version = "0.4.3" }
|
||||||
heck = { version = "0.5.0" }
|
heck = { version = "0.5.0" }
|
||||||
libc = { version = "0.2" }
|
libc = { version = "0.2" }
|
||||||
uds = { git = "https://github.com/rosenpass/uds" }
|
uds = { git = "https://github.com/rosenpass/uds" }
|
||||||
signal-hook = "0.3.17"
|
signal-hook = "0.3.17"
|
||||||
|
lazy_static = "1.5"
|
||||||
|
|
||||||
#Dev dependencies
|
#Dev dependencies
|
||||||
serial_test = "3.2.0"
|
serial_test = "3.2.0"
|
||||||
|
|||||||
@@ -24,6 +24,19 @@ experiment_libcrux_chachapoly_test = [
|
|||||||
"dep:libcrux",
|
"dep:libcrux",
|
||||||
]
|
]
|
||||||
experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"]
|
experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"]
|
||||||
|
bench = [
|
||||||
|
"dep:thiserror",
|
||||||
|
"dep:rand",
|
||||||
|
"dep:libcrux",
|
||||||
|
"dep:libcrux-blake2",
|
||||||
|
"dep:libcrux-ml-kem",
|
||||||
|
"dep:libcrux-chacha20poly1305",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "primitives"
|
||||||
|
harness = false
|
||||||
|
required-features = ["bench"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
@@ -50,3 +63,4 @@ libcrux = { workspace = true, optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
criterion = { workspace = true }
|
||||||
|
|||||||
346
ciphers/benches/primitives.rs
Normal file
346
ciphers/benches/primitives.rs
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
criterion::criterion_main!(keyed_hash::benches, aead::benches, kem::benches);
|
||||||
|
|
||||||
|
fn benchid(base: KvPairs, last: KvPairs) -> String {
|
||||||
|
format!("{base},{last}")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct KvPair<'a>(&'a str, &'a str);
|
||||||
|
|
||||||
|
impl std::fmt::Display for KvPair<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{k}={v}", k = self.0, v = self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct KvPairs<'a>(&'a [KvPair<'a>]);
|
||||||
|
|
||||||
|
impl std::fmt::Display for KvPairs<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self.0.len() {
|
||||||
|
0 => Ok(()),
|
||||||
|
1 => write!(f, "{}", &self.0[0]),
|
||||||
|
_ => {
|
||||||
|
let mut delim = "";
|
||||||
|
for pair in self.0 {
|
||||||
|
write!(f, "{delim}{pair}")?;
|
||||||
|
delim = ",";
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod kem {
|
||||||
|
criterion::criterion_group!(
|
||||||
|
benches,
|
||||||
|
bench_kyber512_libcrux,
|
||||||
|
bench_kyber512_oqs,
|
||||||
|
bench_classicmceliece460896_oqs
|
||||||
|
);
|
||||||
|
|
||||||
|
use criterion::Criterion;
|
||||||
|
|
||||||
|
fn bench_classicmceliece460896_oqs(c: &mut Criterion) {
|
||||||
|
template(
|
||||||
|
c,
|
||||||
|
"classicmceliece460896",
|
||||||
|
"oqs",
|
||||||
|
rosenpass_oqs::ClassicMceliece460896,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_kyber512_libcrux(c: &mut Criterion) {
|
||||||
|
template(
|
||||||
|
c,
|
||||||
|
"kyber512",
|
||||||
|
"libcrux",
|
||||||
|
rosenpass_ciphers::subtle::libcrux::kyber512::Kyber512,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_kyber512_oqs(c: &mut Criterion) {
|
||||||
|
template(c, "kyber512", "oqs", rosenpass_oqs::Kyber512);
|
||||||
|
}
|
||||||
|
|
||||||
|
use rosenpass_cipher_traits::primitives::Kem;
|
||||||
|
|
||||||
|
fn template<
|
||||||
|
const SK_LEN: usize,
|
||||||
|
const PK_LEN: usize,
|
||||||
|
const CT_LEN: usize,
|
||||||
|
const SHK_LEN: usize,
|
||||||
|
T: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN>,
|
||||||
|
>(
|
||||||
|
c: &mut Criterion,
|
||||||
|
alg_name: &str,
|
||||||
|
impl_name: &str,
|
||||||
|
scheme: T,
|
||||||
|
) {
|
||||||
|
use super::{benchid, KvPair, KvPairs};
|
||||||
|
|
||||||
|
let base = [
|
||||||
|
KvPair("primitive", "kem"),
|
||||||
|
KvPair("algorithm", alg_name),
|
||||||
|
KvPair("implementation", impl_name),
|
||||||
|
KvPair("length", "-1"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let kem_benchid = |op| benchid(KvPairs(&base), KvPairs(&[KvPair("operation", op)]));
|
||||||
|
|
||||||
|
c.bench_function(&kem_benchid("keygen"), |bench| {
|
||||||
|
let mut sk = [0; SK_LEN];
|
||||||
|
let mut pk = [0; PK_LEN];
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
scheme.keygen(&mut sk, &mut pk).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function(&kem_benchid("encaps"), |bench| {
|
||||||
|
let mut sk = [0; SK_LEN];
|
||||||
|
let mut pk = [0; PK_LEN];
|
||||||
|
let mut ct = [0; CT_LEN];
|
||||||
|
let mut shk = [0; SHK_LEN];
|
||||||
|
|
||||||
|
scheme.keygen(&mut sk, &mut pk).unwrap();
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
scheme.encaps(&mut shk, &mut ct, &pk).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function(&kem_benchid("decaps"), |bench| {
|
||||||
|
let mut sk = [0; SK_LEN];
|
||||||
|
let mut pk = [0; PK_LEN];
|
||||||
|
let mut ct = [0; CT_LEN];
|
||||||
|
let mut shk = [0; SHK_LEN];
|
||||||
|
let mut shk2 = [0; SHK_LEN];
|
||||||
|
|
||||||
|
scheme.keygen(&mut sk, &mut pk).unwrap();
|
||||||
|
scheme.encaps(&mut shk, &mut ct, &pk).unwrap();
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
scheme.decaps(&mut shk2, &sk, &ct).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mod aead {
|
||||||
|
criterion::criterion_group!(
|
||||||
|
benches,
|
||||||
|
bench_chachapoly_libcrux,
|
||||||
|
bench_chachapoly_rustcrypto,
|
||||||
|
bench_xchachapoly_rustcrypto,
|
||||||
|
);
|
||||||
|
|
||||||
|
use criterion::Criterion;
|
||||||
|
|
||||||
|
const KEY_LEN: usize = rosenpass_ciphers::Aead::KEY_LEN;
|
||||||
|
const TAG_LEN: usize = rosenpass_ciphers::Aead::TAG_LEN;
|
||||||
|
|
||||||
|
fn bench_xchachapoly_rustcrypto(c: &mut Criterion) {
|
||||||
|
template(
|
||||||
|
c,
|
||||||
|
"xchacha20poly1305",
|
||||||
|
"rustcrypto",
|
||||||
|
rosenpass_ciphers::subtle::rust_crypto::xchacha20poly1305_ietf::XChaCha20Poly1305,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_chachapoly_rustcrypto(c: &mut Criterion) {
|
||||||
|
template(
|
||||||
|
c,
|
||||||
|
"chacha20poly1305",
|
||||||
|
"rustcrypto",
|
||||||
|
rosenpass_ciphers::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_chachapoly_libcrux(c: &mut Criterion) {
|
||||||
|
template(
|
||||||
|
c,
|
||||||
|
"chacha20poly1305",
|
||||||
|
"libcrux",
|
||||||
|
rosenpass_ciphers::subtle::libcrux::chacha20poly1305_ietf::ChaCha20Poly1305,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
use rosenpass_cipher_traits::primitives::Aead;
|
||||||
|
|
||||||
|
fn template<const NONCE_LEN: usize, T: Aead<KEY_LEN, NONCE_LEN, TAG_LEN>>(
|
||||||
|
c: &mut Criterion,
|
||||||
|
alg_name: &str,
|
||||||
|
impl_name: &str,
|
||||||
|
scheme: T,
|
||||||
|
) {
|
||||||
|
use crate::{benchid, KvPair, KvPairs};
|
||||||
|
|
||||||
|
let base = [
|
||||||
|
KvPair("primitive", "aead"),
|
||||||
|
KvPair("algorithm", alg_name),
|
||||||
|
KvPair("implementation", impl_name),
|
||||||
|
];
|
||||||
|
let aead_benchid = |op, len| {
|
||||||
|
benchid(
|
||||||
|
KvPairs(&base),
|
||||||
|
KvPairs(&[KvPair("operation", op), KvPair("length", len)]),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let key = [12; KEY_LEN];
|
||||||
|
let nonce = [23; NONCE_LEN];
|
||||||
|
let ad = [];
|
||||||
|
|
||||||
|
c.bench_function(&aead_benchid("encrypt", "32byte"), |bench| {
|
||||||
|
const DATA_LEN: usize = 32;
|
||||||
|
|
||||||
|
let ptxt = [34u8; DATA_LEN];
|
||||||
|
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function(&aead_benchid("decrypt", "32byte"), |bench| {
|
||||||
|
const DATA_LEN: usize = 32;
|
||||||
|
|
||||||
|
let ptxt = [34u8; DATA_LEN];
|
||||||
|
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||||
|
let mut ptxt_out = [0u8; DATA_LEN];
|
||||||
|
|
||||||
|
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
scheme
|
||||||
|
.decrypt(&mut ptxt_out, &key, &nonce, &ad, &mut ctxt)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function(&aead_benchid("encrypt", "1024byte"), |bench| {
|
||||||
|
const DATA_LEN: usize = 1024;
|
||||||
|
|
||||||
|
let ptxt = [34u8; DATA_LEN];
|
||||||
|
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
c.bench_function(&aead_benchid("decrypt", "1024byte"), |bench| {
|
||||||
|
const DATA_LEN: usize = 1024;
|
||||||
|
|
||||||
|
let ptxt = [34u8; DATA_LEN];
|
||||||
|
let mut ctxt = [0; DATA_LEN + TAG_LEN];
|
||||||
|
let mut ptxt_out = [0u8; DATA_LEN];
|
||||||
|
|
||||||
|
scheme.encrypt(&mut ctxt, &key, &nonce, &ad, &ptxt).unwrap();
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
scheme
|
||||||
|
.decrypt(&mut ptxt_out, &key, &nonce, &ad, &mut ctxt)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod keyed_hash {
|
||||||
|
criterion::criterion_group!(
|
||||||
|
benches,
|
||||||
|
bench_blake2b_rustcrypto,
|
||||||
|
bench_blake2b_libcrux,
|
||||||
|
bench_shake256_rustcrypto,
|
||||||
|
);
|
||||||
|
|
||||||
|
const KEY_LEN: usize = 32;
|
||||||
|
const HASH_LEN: usize = 32;
|
||||||
|
|
||||||
|
use criterion::Criterion;
|
||||||
|
|
||||||
|
fn bench_shake256_rustcrypto(c: &mut Criterion) {
|
||||||
|
template(
|
||||||
|
c,
|
||||||
|
"shake256",
|
||||||
|
"rustcrypto",
|
||||||
|
&rosenpass_ciphers::subtle::rust_crypto::keyed_shake256::SHAKE256Core,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_blake2b_rustcrypto(c: &mut Criterion) {
|
||||||
|
template(
|
||||||
|
c,
|
||||||
|
"blake2b",
|
||||||
|
"rustcrypto",
|
||||||
|
&rosenpass_ciphers::subtle::rust_crypto::blake2b::Blake2b,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_blake2b_libcrux(c: &mut Criterion) {
|
||||||
|
template(
|
||||||
|
c,
|
||||||
|
"blake2b",
|
||||||
|
"libcrux",
|
||||||
|
&rosenpass_ciphers::subtle::libcrux::blake2b::Blake2b,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
use rosenpass_cipher_traits::primitives::KeyedHash;
|
||||||
|
|
||||||
|
fn template<H: KeyedHash<KEY_LEN, HASH_LEN>>(
|
||||||
|
c: &mut Criterion,
|
||||||
|
alg_name: &str,
|
||||||
|
impl_name: &str,
|
||||||
|
_: &H,
|
||||||
|
) where
|
||||||
|
H::Error: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
use crate::{benchid, KvPair, KvPairs};
|
||||||
|
|
||||||
|
let key = [12u8; KEY_LEN];
|
||||||
|
let mut out = [0u8; HASH_LEN];
|
||||||
|
|
||||||
|
let base = [
|
||||||
|
KvPair("primitive", "keyedhash"),
|
||||||
|
KvPair("algorithm", alg_name),
|
||||||
|
KvPair("implementation", impl_name),
|
||||||
|
KvPair("operation", "hash"),
|
||||||
|
];
|
||||||
|
let keyedhash_benchid = |len| benchid(KvPairs(&base), KvPairs(&[KvPair("length", len)]));
|
||||||
|
|
||||||
|
c.bench_function(&keyedhash_benchid("32byte"), |bench| {
|
||||||
|
let bytes = [34u8; 32];
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.bench_function(&keyedhash_benchid("64byte"), |bench| {
|
||||||
|
let bytes = [34u8; 64];
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.bench_function(&keyedhash_benchid("128byte"), |bench| {
|
||||||
|
let bytes = [34u8; 128];
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.bench_function(&keyedhash_benchid("1024byte"), |bench| {
|
||||||
|
let bytes = [34u8; 1024];
|
||||||
|
|
||||||
|
bench.iter(|| {
|
||||||
|
H::keyed_hash(&key, &bytes, &mut out).unwrap();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod templates {}
|
||||||
@@ -4,11 +4,11 @@
|
|||||||
//!
|
//!
|
||||||
//! [Github](https://github.com/cryspen/libcrux)
|
//! [Github](https://github.com/cryspen/libcrux)
|
||||||
|
|
||||||
#[cfg(feature = "experiment_libcrux_blake2")]
|
#[cfg(any(feature = "experiment_libcrux_blake2", feature = "bench"))]
|
||||||
pub mod blake2b;
|
pub mod blake2b;
|
||||||
|
|
||||||
#[cfg(feature = "experiment_libcrux_chachapoly")]
|
#[cfg(any(feature = "experiment_libcrux_chachapoly", feature = "bench"))]
|
||||||
pub mod chacha20poly1305_ietf;
|
pub mod chacha20poly1305_ietf;
|
||||||
|
|
||||||
#[cfg(feature = "experiment_libcrux_kyber")]
|
#[cfg(any(feature = "experiment_libcrux_kyber", feature = "bench"))]
|
||||||
pub mod kyber512;
|
pub mod kyber512;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub mod rust_crypto;
|
|||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "experiment_libcrux_blake2",
|
feature = "experiment_libcrux_blake2",
|
||||||
feature = "experiment_libcrux_chachapoly",
|
feature = "experiment_libcrux_chachapoly",
|
||||||
feature = "experiment_libcrux_kyber"
|
feature = "experiment_libcrux_kyber",
|
||||||
|
feature = "bench"
|
||||||
))]
|
))]
|
||||||
pub mod libcrux;
|
pub mod libcrux;
|
||||||
|
|||||||
10
flake.nix
10
flake.nix
@@ -89,6 +89,7 @@
|
|||||||
[
|
[
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
"aarch64-linux"
|
"aarch64-linux"
|
||||||
|
"i686-linux"
|
||||||
]
|
]
|
||||||
(
|
(
|
||||||
system:
|
system:
|
||||||
@@ -172,6 +173,15 @@
|
|||||||
inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA;
|
inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
devShells.benchmark = pkgs.mkShell {
|
||||||
|
inputsFrom = [ pkgs.rosenpass ];
|
||||||
|
nativeBuildInputs = let
|
||||||
|
rustToolchain = (inputs.fenix.packages.${system}.toolchainOf {
|
||||||
|
channel = "1.77.0";
|
||||||
|
sha256 = "sha256-+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8=";
|
||||||
|
});
|
||||||
|
in [ rustToolchain.toolchain ];
|
||||||
|
};
|
||||||
|
|
||||||
checks =
|
checks =
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ rustPlatform.buildRustPackage {
|
|||||||
"memsec-0.6.3" = "sha256-4ri+IEqLd77cLcul3lZrmpDKj4cwuYJ8oPRAiQNGeLw=";
|
"memsec-0.6.3" = "sha256-4ri+IEqLd77cLcul3lZrmpDKj4cwuYJ8oPRAiQNGeLw=";
|
||||||
"uds-0.4.2" = "sha256-qlxr/iJt2AV4WryePIvqm/8/MK/iqtzegztNliR93W8=";
|
"uds-0.4.2" = "sha256-qlxr/iJt2AV4WryePIvqm/8/MK/iqtzegztNliR93W8=";
|
||||||
"libcrux-blake2-0.0.3-pre" = "sha256-0CLjuzwJqGooiODOHf5D8Hc8ClcG/XcGvVGyOVnLmJY=";
|
"libcrux-blake2-0.0.3-pre" = "sha256-0CLjuzwJqGooiODOHf5D8Hc8ClcG/XcGvVGyOVnLmJY=";
|
||||||
|
"libcrux-macros-0.0.3" = "sha256-Tb5uRirwhRhoFEK8uu1LvXl89h++40pxzZ+7kXe8RAI=";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
42
readme.md
42
readme.md
@@ -1,3 +1,37 @@
|
|||||||
|
# Changes on This Branch
|
||||||
|
|
||||||
|
This branch adds facilities for benchmarking both the Rosenpass protocol
|
||||||
|
code and the implementations of the primitives behind it. The primitives
|
||||||
|
are benchmarked using criterion. For the protocol code, we use a custom
|
||||||
|
library for instrumenting the code such that events are written to a
|
||||||
|
trace, which is then inspected after a run.
|
||||||
|
|
||||||
|
## Protocol Benchmark
|
||||||
|
|
||||||
|
The trace that is being written to lives in a new module
|
||||||
|
`trace_bench` in the util crate. A basic benchmark that
|
||||||
|
performs some minor statistical analysis of the trace can be run using
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo bench -p rosenpass --bench trace_handshake -F trace_bench
|
||||||
|
```
|
||||||
|
|
||||||
|
## Primitive Benchmark
|
||||||
|
|
||||||
|
Benchmarks for the functions of the traits `Kem`, `Aead` and `KeyedHash`
|
||||||
|
have been added and are run for all implementations in the `primitives`
|
||||||
|
benchmark of `rosenpass-ciphers`. Run the benchmarks using
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo bench -p rosenpass-ciphers --bench primitives -F bench
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the `bench` feature enables the inclusion of the libcrux-backed
|
||||||
|
trait implementations in the module tree, but does not enable them
|
||||||
|
as default.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Rosenpass README
|
# Rosenpass README
|
||||||
|
|
||||||

|

|
||||||
@@ -14,7 +48,7 @@ This repository contains
|
|||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
First, [install rosenpass](#Getting-Rosenpass). Then, check out the help functions of `rp` & `rosenpass`:
|
First, [install rosenpass](#getting-rosenpass). Then, check out the help functions of `rp` & `rosenpass`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
rp help
|
rp help
|
||||||
@@ -64,11 +98,7 @@ The analysis is implemented according to modern software engineering principles:
|
|||||||
The code uses a variety of optimizations to speed up analysis such as using secret functions to model trusted/malicious setup. We split the model into two separate entry points which can be analyzed in parallel. Each is much faster than both models combined.
|
The code uses a variety of optimizations to speed up analysis such as using secret functions to model trusted/malicious setup. We split the model into two separate entry points which can be analyzed in parallel. Each is much faster than both models combined.
|
||||||
A wrapper script provides instant feedback about which queries execute as expected in color: A red cross if a query fails and a green check if it succeeds.
|
A wrapper script provides instant feedback about which queries execute as expected in color: A red cross if a query fails and a green check if it succeeds.
|
||||||
|
|
||||||
[^liboqs]: https://openquantumsafe.org/liboqs/
|
[^liboqs]: <https://openquantumsafe.org/liboqs/>
|
||||||
[^wg]: https://www.wireguard.com/
|
|
||||||
[^pqwg]: https://eprint.iacr.org/2020/379
|
|
||||||
[^pqwg-statedis]: Unless supplied with a pre-shared-key, but this defeats the purpose of a key exchange protocol
|
|
||||||
[^wg-statedis]: https://lists.zx2c4.com/pipermail/wireguard/2021-August/006916.htmlA
|
|
||||||
|
|
||||||
# Getting Rosenpass
|
# Getting Rosenpass
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ required-features = [
|
|||||||
"internal_bin_gen_ipc_msg_types",
|
"internal_bin_gen_ipc_msg_types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "trace_handshake"
|
||||||
|
harness = false
|
||||||
|
required-features = ["trace_bench"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "handshake"
|
name = "handshake"
|
||||||
harness = false
|
harness = false
|
||||||
@@ -72,6 +77,7 @@ command-fds = { workspace = true, optional = true }
|
|||||||
rustix = { workspace = true, optional = true }
|
rustix = { workspace = true, optional = true }
|
||||||
uds = { workspace = true, optional = true, features = ["mio_1xx"] }
|
uds = { workspace = true, optional = true, features = ["mio_1xx"] }
|
||||||
signal-hook = { workspace = true, optional = true }
|
signal-hook = { workspace = true, optional = true }
|
||||||
|
libcrux-test-utils = { workspace = true, optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
@@ -106,6 +112,7 @@ experiment_api = [
|
|||||||
internal_signal_handling_for_coverage_reports = ["signal-hook"]
|
internal_signal_handling_for_coverage_reports = ["signal-hook"]
|
||||||
internal_testing = []
|
internal_testing = []
|
||||||
internal_bin_gen_ipc_msg_types = ["hex", "heck"]
|
internal_bin_gen_ipc_msg_types = ["hex", "heck"]
|
||||||
|
trace_bench = ["rosenpass-util/trace_bench", "dep:libcrux-test-utils"]
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }
|
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }
|
||||||
|
|||||||
371
rosenpass/benches/trace_handshake.rs
Normal file
371
rosenpass/benches/trace_handshake.rs
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
// Standard library imports
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
hint::black_box,
|
||||||
|
io::{self, Write},
|
||||||
|
ops::DerefMut,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
// External crate imports
|
||||||
|
use anyhow::Result;
|
||||||
|
use libcrux_test_utils::tracing::{EventType, Trace as _};
|
||||||
|
use rosenpass::protocol::{
|
||||||
|
CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey,
|
||||||
|
};
|
||||||
|
use rosenpass_cipher_traits::primitives::Kem;
|
||||||
|
use rosenpass_ciphers::StaticKem;
|
||||||
|
use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets;
|
||||||
|
use rosenpass_util::trace_bench::{RpEventType, TRACE};
|
||||||
|
|
||||||
|
const ITERATIONS: usize = 100;
|
||||||
|
|
||||||
|
fn handle(
|
||||||
|
tx: &mut CryptoServer,
|
||||||
|
msgb: &mut MsgBuf,
|
||||||
|
msgl: usize,
|
||||||
|
rx: &mut CryptoServer,
|
||||||
|
resb: &mut MsgBuf,
|
||||||
|
) -> Result<(Option<SymKey>, Option<SymKey>)> {
|
||||||
|
let HandleMsgResult {
|
||||||
|
exchanged_with: xch,
|
||||||
|
resp,
|
||||||
|
} = rx.handle_msg(&msgb[..msgl], &mut **resb)?;
|
||||||
|
|
||||||
|
assert!(matches!(xch, None | Some(PeerPtr(0))));
|
||||||
|
|
||||||
|
let xch = xch.map(|p| rx.osk(p).unwrap());
|
||||||
|
|
||||||
|
let (rxk, txk) = resp
|
||||||
|
.map(|resl| handle(rx, resb, resl, tx, msgb))
|
||||||
|
.transpose()?
|
||||||
|
.unwrap_or((None, None));
|
||||||
|
|
||||||
|
assert!(rxk.is_none() || xch.is_none());
|
||||||
|
|
||||||
|
Ok((txk, rxk.or(xch)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hs(ini: &mut CryptoServer, res: &mut CryptoServer) -> Result<()> {
|
||||||
|
let (mut inib, mut resb) = (MsgBuf::zero(), MsgBuf::zero());
|
||||||
|
let sz = ini.initiate_handshake(PeerPtr(0), &mut *inib)?;
|
||||||
|
let (kini, kres) = handle(ini, &mut inib, sz, res, &mut resb)?;
|
||||||
|
assert!(kini.unwrap().secret() == kres.unwrap().secret());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keygen() -> Result<(SSk, SPk)> {
|
||||||
|
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||||
|
StaticKem.keygen(sk.secret_mut(), pk.deref_mut())?;
|
||||||
|
Ok((sk, pk))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer)> {
|
||||||
|
let psk = SymKey::random();
|
||||||
|
let ((ska, pka), (skb, pkb)) = (keygen()?, keygen()?);
|
||||||
|
let (mut a, mut b) = (
|
||||||
|
CryptoServer::new(ska, pka.clone()),
|
||||||
|
CryptoServer::new(skb, pkb.clone()),
|
||||||
|
);
|
||||||
|
a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?;
|
||||||
|
b.add_peer(Some(psk), pka, protocol_version)?;
|
||||||
|
Ok((a, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Attempt to use memfd_secrets for storing sensitive key material
|
||||||
|
secret_policy_try_use_memfd_secrets();
|
||||||
|
|
||||||
|
// Run protocol for V02
|
||||||
|
let (mut a_v02, mut b_v02) = make_server_pair(ProtocolVersion::V02).unwrap();
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
hs(black_box(&mut a_v02), black_box(&mut b_v02)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit a marker event to separate V02 and V03 trace sections
|
||||||
|
TRACE.emit_on_the_fly("start-hs-v03");
|
||||||
|
|
||||||
|
// Run protocol for V03
|
||||||
|
let (mut a_v03, mut b_v03) = make_server_pair(ProtocolVersion::V03).unwrap();
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
hs(black_box(&mut a_v03), black_box(&mut b_v03)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the trace events generated during the handshakes
|
||||||
|
let trace: Vec<_> = TRACE.clone().report();
|
||||||
|
|
||||||
|
// Split the trace into V02 and V03 sections based on the marker
|
||||||
|
let (trace_v02, trace_v03) = {
|
||||||
|
let cutoff = trace
|
||||||
|
.iter()
|
||||||
|
.position(|entry| entry.label == "start-hs-v03")
|
||||||
|
.unwrap();
|
||||||
|
// Exclude the marker itself from the V03 trace
|
||||||
|
let (v02, v03_with_marker) = trace.split_at(cutoff);
|
||||||
|
(v02, &v03_with_marker[1..])
|
||||||
|
};
|
||||||
|
|
||||||
|
// Perform statistical analysis on both trace sections and write results as JSON
|
||||||
|
write_json_arrays(
|
||||||
|
&mut std::io::stdout(), // Write to standard output
|
||||||
|
vec![
|
||||||
|
("V02", statistical_analysis(trace_v02.to_vec())),
|
||||||
|
("V03", statistical_analysis(trace_v03.to_vec())),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.expect("error writing json data");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a vector of trace events, bins them by label, extracts durations,
|
||||||
|
/// filters empty bins, calculates aggregate statistics (mean, std dev), and returns them.
|
||||||
|
fn statistical_analysis(trace: Vec<RpEventType>) -> Vec<(&'static str, AggregateStat<Duration>)> {
|
||||||
|
bin_events(trace)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(label, spans)| (label, extract_span_durations(label, spans.as_slice())))
|
||||||
|
.filter(|(_, durations)| !durations.is_empty())
|
||||||
|
.map(|(label, durations)| (label, AggregateStat::analyze_durations(&durations)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes an iterator of ("protocol_version", iterator_of_stats) pairs and writes them
|
||||||
|
/// as a single flat JSON array to the provided writer.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `w` - The writer to output JSON to (e.g., stdout, file).
|
||||||
|
/// * `item_groups` - An iterator producing tuples of (`&'static str`, `II`), where
|
||||||
|
/// `II` is itself an iterator producing (`&'static str`, `AggregateStat<Duration>`).
|
||||||
|
/// Represents the protocol_version name and the statistics items within that protocol_version.
|
||||||
|
///
|
||||||
|
/// # Type Parameters
|
||||||
|
/// * `W` - A type that implements `std::io::Write`.
|
||||||
|
/// * `II` - An iterator type yielding (`&'static str`, `AggregateStat<Duration>`).
|
||||||
|
fn write_json_arrays<W: Write, II: IntoIterator<Item = (&'static str, AggregateStat<Duration>)>>(
|
||||||
|
w: &mut W,
|
||||||
|
item_groups: impl IntoIterator<Item = (&'static str, II)>,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
// Flatten the groups into a single iterator of (protocol_version, label, stats)
|
||||||
|
let iter = item_groups.into_iter().flat_map(|(version, items)| {
|
||||||
|
items
|
||||||
|
.into_iter()
|
||||||
|
.map(move |(label, agg_stat)| (version, label, agg_stat))
|
||||||
|
});
|
||||||
|
let mut delim = ""; // Start with no delimiter
|
||||||
|
|
||||||
|
// Start the JSON array
|
||||||
|
write!(w, "[")?;
|
||||||
|
|
||||||
|
// Write the flattened statistics as JSON objects, separated by commas.
|
||||||
|
for (version, label, agg_stat) in iter {
|
||||||
|
write!(w, "{delim}")?; // Write delimiter (empty for first item, "," for subsequent)
|
||||||
|
agg_stat.write_json_ns(label, version, w)?; // Write the JSON object for the stat entry
|
||||||
|
delim = ","; // Set delimiter for the next iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
// End the JSON array
|
||||||
|
write!(w, "]")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to group benchmark results in visualizations
|
||||||
|
enum RunTimeGroup {
|
||||||
|
/// For particularly long operations.
|
||||||
|
Long,
|
||||||
|
/// Operations of moderate duration.
|
||||||
|
Medium,
|
||||||
|
/// Operations expected to complete under a millisecond.
|
||||||
|
BelowMillisec,
|
||||||
|
/// Very fast operations, likely under a microsecond.
|
||||||
|
BelowMicrosec,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for RunTimeGroup {
|
||||||
|
/// Used when writing the group information to JSON output.
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let txt = match self {
|
||||||
|
RunTimeGroup::Long => "long",
|
||||||
|
RunTimeGroup::Medium => "medium",
|
||||||
|
RunTimeGroup::BelowMillisec => "below_ms",
|
||||||
|
RunTimeGroup::BelowMicrosec => "below_us",
|
||||||
|
};
|
||||||
|
write!(f, "{txt}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps specific internal timing labels (likely from rosenpass internals)
|
||||||
|
/// to the broader SpanGroup categories.
|
||||||
|
fn run_time_group(label: &str) -> RunTimeGroup {
|
||||||
|
match label {
|
||||||
|
// Explicitly categorized labels based on expected performance characteristics
|
||||||
|
"handle_init_hello" | "handle_resp_hello" | "RHI5" | "IHR5" => RunTimeGroup::Long,
|
||||||
|
"RHR1" | "IHI2" | "ICR6" => RunTimeGroup::BelowMicrosec,
|
||||||
|
"RHI6" | "ICI7" | "ICR7" | "RHR3" | "ICR3" | "IHR8" | "ICI4" | "RHI3" | "RHI4" | "RHR4"
|
||||||
|
| "RHR7" | "ICI3" | "IHI3" | "IHI8" | "ICR2" | "ICR4" | "IHR4" | "IHR6" | "IHI4"
|
||||||
|
| "RHI7" => RunTimeGroup::BelowMillisec,
|
||||||
|
// Default protocol_version for any other labels
|
||||||
|
_ => RunTimeGroup::Medium,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used temporarily within `extract_span_durations` to track open spans
|
||||||
|
/// and calculated durations.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum StatEntry {
|
||||||
|
/// Represents an unmatched SpanOpen event with its timestamp.
|
||||||
|
Start(Instant),
|
||||||
|
/// Represents a completed span with its calculated duration.
|
||||||
|
Duration(Duration),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a flat list of events and organizes them into a HashMap where keys
|
||||||
|
/// are event labels and values are vectors of events with that label.
|
||||||
|
fn bin_events(events: Vec<RpEventType>) -> HashMap<&'static str, Vec<RpEventType>> {
|
||||||
|
let mut spans = HashMap::<_, Vec<_>>::new();
|
||||||
|
for event in events {
|
||||||
|
// Get the vector for the event's label, or create a new one
|
||||||
|
let spans_for_label = spans.entry(event.label).or_default();
|
||||||
|
// Add the event to the vector
|
||||||
|
spans_for_label.push(event);
|
||||||
|
}
|
||||||
|
spans
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes a list of events (assumed to be for the same label), matching
|
||||||
|
/// `SpanOpen` and `SpanClose` events to calculate the duration of each span.
|
||||||
|
/// It handles potentially interleaved spans correctly.
|
||||||
|
fn extract_span_durations(label: &str, events: &[RpEventType]) -> Vec<Duration> {
|
||||||
|
let mut processing_list: Vec<StatEntry> = vec![]; // List to track open spans and final durations
|
||||||
|
|
||||||
|
for entry in events {
|
||||||
|
match &entry.ty {
|
||||||
|
EventType::SpanOpen => {
|
||||||
|
// Record the start time of a new span
|
||||||
|
processing_list.push(StatEntry::Start(entry.at));
|
||||||
|
}
|
||||||
|
EventType::SpanClose => {
|
||||||
|
// Find the most recent unmatched 'Start' entry
|
||||||
|
let start_index = processing_list
|
||||||
|
.iter()
|
||||||
|
.rposition(|span| matches!(span, StatEntry::Start(_))); // Find last Start
|
||||||
|
|
||||||
|
match start_index {
|
||||||
|
Some(index) => {
|
||||||
|
// Retrieve the start time
|
||||||
|
let start_time = match processing_list[index] {
|
||||||
|
StatEntry::Start(t) => t,
|
||||||
|
_ => unreachable!(), // Should always be Start based on rposition logic
|
||||||
|
};
|
||||||
|
// Calculate duration and replace the 'Start' entry with 'Duration'
|
||||||
|
processing_list[index] = StatEntry::Duration(entry.at - start_time);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// This should not happen with well-formed traces
|
||||||
|
eprintln!(
|
||||||
|
"Warning: Found SpanClose without a matching SpanOpen for label '{}': {:?}",
|
||||||
|
label, entry
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EventType::OnTheFly => {
|
||||||
|
// Ignore OnTheFly events for duration calculation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all calculated durations, reporting any unmatched starts
|
||||||
|
processing_list
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|span| match span {
|
||||||
|
StatEntry::Start(at) => {
|
||||||
|
// Report error if a span was opened but never closed
|
||||||
|
eprintln!(
|
||||||
|
"Warning: Unmatched SpanOpen at {:?} for label '{}'",
|
||||||
|
at, label
|
||||||
|
);
|
||||||
|
None // Discard unmatched starts
|
||||||
|
}
|
||||||
|
StatEntry::Duration(dur) => Some(dur), // Keep calculated durations
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores the mean, standard deviation, relative standard deviation (sd/mean),
|
||||||
|
/// and the number of samples used for calculation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct AggregateStat<T> {
|
||||||
|
/// Average duration.
|
||||||
|
mean_duration: T,
|
||||||
|
/// Standard deviation of durations.
|
||||||
|
sd_duration: T,
|
||||||
|
/// Standard deviation as a percentage of the mean.
|
||||||
|
sd_by_mean: String,
|
||||||
|
/// Number of duration measurements.
|
||||||
|
sample_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AggregateStat<Duration> {
|
||||||
|
/// Calculates mean, variance, and standard deviation for a slice of Durations.
|
||||||
|
fn analyze_durations(durations: &[Duration]) -> Self {
|
||||||
|
let sample_size = durations.len();
|
||||||
|
assert!(sample_size > 0, "Cannot analyze empty duration slice");
|
||||||
|
|
||||||
|
// Calculate the sum of durations
|
||||||
|
let sum: Duration = durations.iter().sum();
|
||||||
|
// Calculate the mean duration
|
||||||
|
let mean = sum / (sample_size as u32);
|
||||||
|
|
||||||
|
// Calculate mean in nanoseconds, adding 1 to avoid potential division by zero later
|
||||||
|
// (though highly unlikely with realistic durations)
|
||||||
|
let mean_ns = mean.as_nanos().saturating_add(1);
|
||||||
|
|
||||||
|
// Calculate variance (sum of squared differences from the mean) / N
|
||||||
|
let variance = durations
|
||||||
|
.iter()
|
||||||
|
.map(Duration::as_nanos)
|
||||||
|
.map(|d_ns| d_ns.abs_diff(mean_ns).pow(2)) // (duration_ns - mean_ns)^2
|
||||||
|
.sum::<u128>() // Sum of squares
|
||||||
|
/ (sample_size as u128); // Divide by sample size
|
||||||
|
|
||||||
|
// Calculate standard deviation (sqrt of variance)
|
||||||
|
let sd_ns = (variance as f64).sqrt() as u128;
|
||||||
|
let sd = Duration::from_nanos(sd_ns as u64); // Convert back to Duration
|
||||||
|
|
||||||
|
// Calculate relative standard deviation (sd / mean) as a percentage string
|
||||||
|
let sd_rel_permille = (10000 * sd_ns).checked_div(mean_ns).unwrap_or(0); // Calculate sd/mean * 10000
|
||||||
|
let sd_rel_formatted = format!("{}.{:02}%", sd_rel_permille / 100, sd_rel_permille % 100);
|
||||||
|
|
||||||
|
AggregateStat {
|
||||||
|
mean_duration: mean,
|
||||||
|
sd_duration: sd,
|
||||||
|
sd_by_mean: sd_rel_formatted,
|
||||||
|
sample_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the statistics as a JSON object to the provided writer.
|
||||||
|
/// Includes metadata like label, protocol_version, OS, architecture, and run time group.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `label` - The specific benchmark/span label.
|
||||||
|
/// * `protocol_version` - Version of the protocol that is benchmarked.
|
||||||
|
/// * `w` - The output writer (must implement `std::io::Write`).
|
||||||
|
fn write_json_ns(
|
||||||
|
&self,
|
||||||
|
label: &str,
|
||||||
|
protocol_version: &str,
|
||||||
|
w: &mut impl io::Write,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
// Format the JSON string using measured values and environment constants
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
r#"{{"name":"{name}", "unit":"ns/iter", "value":"{value}", "range":"± {range}", "protocol version":"{protocol_version}", "sample size":"{sample_size}", "os":"{os}", "arch":"{arch}", "run time":"{run_time}"}}"#,
|
||||||
|
name = label, // Benchmark name
|
||||||
|
value = self.mean_duration.as_nanos(), // Mean duration in nanoseconds
|
||||||
|
range = self.sd_duration.as_nanos(), // Standard deviation in nanoseconds
|
||||||
|
sample_size = self.sample_size, // Number of samples
|
||||||
|
os = std::env::consts::OS, // Operating system
|
||||||
|
arch = std::env::consts::ARCH, // CPU architecture
|
||||||
|
run_time = run_time_group(label), // Run time group category (long, medium, etc.)
|
||||||
|
protocol_version = protocol_version // Overall protocol_version (e.g., protocol version)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{bail, ensure, Context, Result};
|
use anyhow::{bail, ensure, Context, Result};
|
||||||
use rand::Fill as Randomize;
|
|
||||||
|
|
||||||
use crate::{hash_domains, msgs::*, RosenpassError};
|
use crate::{hash_domains, msgs::*, RosenpassError};
|
||||||
use memoffset::span_of;
|
use memoffset::span_of;
|
||||||
@@ -3547,9 +3546,27 @@ impl CryptoServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marks a section of the protocol using the same identifiers as are used in the whitepaper.
|
||||||
|
/// When building with the trace benchmarking feature enabled, this also emits span events into the
|
||||||
|
/// trace, which allows reconstructing the run times of the individual sections for performace
|
||||||
|
/// measurement.
|
||||||
|
macro_rules! protocol_section {
|
||||||
|
($label:expr, $body:tt) => {{
|
||||||
|
#[cfg(feature = "trace_bench")]
|
||||||
|
let _span_raii_handle = rosenpass_util::trace_bench::TRACE.emit_span($label);
|
||||||
|
|
||||||
|
#[allow(unused_braces)]
|
||||||
|
$body
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
impl CryptoServer {
|
impl CryptoServer {
|
||||||
/// Core cryptographic protocol implementation: Kicks of the handshake
|
/// Core cryptographic protocol implementation: Kicks of the handshake
|
||||||
/// on the initiator side, producing the InitHello message.
|
/// on the initiator side, producing the InitHello message.
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "trace_bench",
|
||||||
|
rosenpass_util::trace_bench::trace_span("handle_initiation", rosenpass_util::trace_bench::TRACE)
|
||||||
|
)]
|
||||||
pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result<PeerPtr> {
|
pub fn handle_initiation(&mut self, peer: PeerPtr, ih: &mut InitHello) -> Result<PeerPtr> {
|
||||||
let mut hs = InitiatorHandshake::zero_with_timestamp(
|
let mut hs = InitiatorHandshake::zero_with_timestamp(
|
||||||
self,
|
self,
|
||||||
@@ -3557,37 +3574,53 @@ impl CryptoServer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// IHI1
|
// IHI1
|
||||||
hs.core.init(peer.get(self).spkt.deref())?;
|
protocol_section!("IHI1", {
|
||||||
|
hs.core.init(peer.get(self).spkt.deref())?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHI2
|
// IHI2
|
||||||
hs.core.sidi.randomize();
|
protocol_section!("IHI2", {
|
||||||
ih.sidi.copy_from_slice(&hs.core.sidi.value);
|
hs.core.sidi.randomize();
|
||||||
|
ih.sidi.copy_from_slice(&hs.core.sidi.value);
|
||||||
|
});
|
||||||
|
|
||||||
// IHI3
|
// IHI3
|
||||||
EphemeralKem.keygen(hs.eski.secret_mut(), &mut *hs.epki)?;
|
protocol_section!("IHI3", {
|
||||||
ih.epki.copy_from_slice(&hs.epki.value);
|
EphemeralKem.keygen(hs.eski.secret_mut(), &mut *hs.epki)?;
|
||||||
|
ih.epki.copy_from_slice(&hs.epki.value);
|
||||||
|
});
|
||||||
|
|
||||||
// IHI4
|
// IHI4
|
||||||
hs.core.mix(ih.sidi.as_slice())?.mix(ih.epki.as_slice())?;
|
protocol_section!("IHI4", {
|
||||||
|
hs.core.mix(ih.sidi.as_slice())?.mix(ih.epki.as_slice())?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHI5
|
// IHI5
|
||||||
hs.core
|
protocol_section!("IHI5", {
|
||||||
.encaps_and_mix(&StaticKem, &mut ih.sctr, peer.get(self).spkt.deref())?;
|
hs.core
|
||||||
|
.encaps_and_mix(&StaticKem, &mut ih.sctr, peer.get(self).spkt.deref())?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHI6
|
// IHI6
|
||||||
hs.core.encrypt_and_mix(
|
protocol_section!("IHI6", {
|
||||||
ih.pidic.as_mut_slice(),
|
hs.core.encrypt_and_mix(
|
||||||
self.pidm(peer.get(self).protocol_version.keyed_hash())?
|
ih.pidic.as_mut_slice(),
|
||||||
.as_ref(),
|
self.pidm(peer.get(self).protocol_version.keyed_hash())?
|
||||||
)?;
|
.as_ref(),
|
||||||
|
)?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHI7
|
// IHI7
|
||||||
hs.core
|
protocol_section!("IHI7", {
|
||||||
.mix(self.spkm.deref())?
|
hs.core
|
||||||
.mix(peer.get(self).psk.secret())?;
|
.mix(self.spkm.deref())?
|
||||||
|
.mix(peer.get(self).psk.secret())?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHI8
|
// IHI8
|
||||||
hs.core.encrypt_and_mix(ih.auth.as_mut_slice(), &[])?;
|
protocol_section!("IHI8", {
|
||||||
|
hs.core.encrypt_and_mix(ih.auth.as_mut_slice(), &[])?;
|
||||||
|
});
|
||||||
|
|
||||||
// Update the handshake hash last (not changing any state on prior error
|
// Update the handshake hash last (not changing any state on prior error
|
||||||
peer.hs().insert(self, hs)?;
|
peer.hs().insert(self, hs)?;
|
||||||
@@ -3597,6 +3630,10 @@ impl CryptoServer {
|
|||||||
|
|
||||||
/// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a
|
/// Core cryptographic protocol implementation: Parses an [InitHello] message and produces a
|
||||||
/// [RespHello] message on the responder side.
|
/// [RespHello] message on the responder side.
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "trace_bench",
|
||||||
|
rosenpass_util::trace_bench::trace_span("handle_init_hello", rosenpass_util::trace_bench::TRACE)
|
||||||
|
)]
|
||||||
pub fn handle_init_hello(
|
pub fn handle_init_hello(
|
||||||
&mut self,
|
&mut self,
|
||||||
ih: &InitHello,
|
ih: &InitHello,
|
||||||
@@ -3608,54 +3645,80 @@ impl CryptoServer {
|
|||||||
core.sidi = SessionId::from_slice(&ih.sidi);
|
core.sidi = SessionId::from_slice(&ih.sidi);
|
||||||
|
|
||||||
// IHR1
|
// IHR1
|
||||||
core.init(self.spkm.deref())?;
|
protocol_section!("IHR1", {
|
||||||
|
core.init(self.spkm.deref())?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHR4
|
// IHR4
|
||||||
core.mix(&ih.sidi)?.mix(&ih.epki)?;
|
protocol_section!("IHR4", {
|
||||||
|
core.mix(&ih.sidi)?.mix(&ih.epki)?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHR5
|
// IHR5
|
||||||
core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &ih.sctr)?;
|
protocol_section!("IHR5", {
|
||||||
|
core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &ih.sctr)?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHR6
|
// IHR6
|
||||||
let peer = {
|
let peer = protocol_section!("IHR6", {
|
||||||
let mut peerid = PeerId::zero();
|
let mut peerid = PeerId::zero();
|
||||||
core.decrypt_and_mix(&mut *peerid, &ih.pidic)?;
|
core.decrypt_and_mix(&mut *peerid, &ih.pidic)?;
|
||||||
self.find_peer(peerid)
|
self.find_peer(peerid)
|
||||||
.with_context(|| format!("No such peer {peerid:?}."))?
|
.with_context(|| format!("No such peer {peerid:?}."))?
|
||||||
};
|
});
|
||||||
|
|
||||||
// IHR7
|
// IHR7
|
||||||
core.mix(peer.get(self).spkt.deref())?
|
protocol_section!("IHR7", {
|
||||||
.mix(peer.get(self).psk.secret())?;
|
core.mix(peer.get(self).spkt.deref())?
|
||||||
|
.mix(peer.get(self).psk.secret())?;
|
||||||
|
});
|
||||||
|
|
||||||
// IHR8
|
// IHR8
|
||||||
core.decrypt_and_mix(&mut [0u8; 0], &ih.auth)?;
|
protocol_section!("IHR8", {
|
||||||
|
core.decrypt_and_mix(&mut [0u8; 0], &ih.auth)?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHR1
|
// RHR1
|
||||||
core.sidr.randomize();
|
protocol_section!("RHR1", {
|
||||||
rh.sidi.copy_from_slice(core.sidi.as_ref());
|
core.sidr.randomize();
|
||||||
rh.sidr.copy_from_slice(core.sidr.as_ref());
|
rh.sidi.copy_from_slice(core.sidi.as_ref());
|
||||||
|
rh.sidr.copy_from_slice(core.sidr.as_ref());
|
||||||
|
});
|
||||||
|
|
||||||
// RHR3
|
// RHR3
|
||||||
core.mix(&rh.sidr)?.mix(&rh.sidi)?;
|
protocol_section!("RHR3", {
|
||||||
|
core.mix(&rh.sidr)?.mix(&rh.sidi)?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHR4
|
// RHR4
|
||||||
core.encaps_and_mix(&EphemeralKem, &mut rh.ecti, &ih.epki)?;
|
protocol_section!("RHR4", {
|
||||||
|
core.encaps_and_mix(&EphemeralKem, &mut rh.ecti, &ih.epki)?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHR5
|
// RHR5
|
||||||
core.encaps_and_mix(&StaticKem, &mut rh.scti, peer.get(self).spkt.deref())?;
|
protocol_section!("RHR5", {
|
||||||
|
core.encaps_and_mix(&StaticKem, &mut rh.scti, peer.get(self).spkt.deref())?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHR6
|
// RHR6
|
||||||
core.store_biscuit(self, peer, &mut rh.biscuit)?;
|
protocol_section!("RHR6", {
|
||||||
|
core.store_biscuit(self, peer, &mut rh.biscuit)?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHR7
|
// RHR7
|
||||||
core.encrypt_and_mix(&mut rh.auth, &[])?;
|
protocol_section!("RHR7", {
|
||||||
|
core.encrypt_and_mix(&mut rh.auth, &[])?;
|
||||||
|
});
|
||||||
|
|
||||||
Ok(peer)
|
Ok(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Core cryptographic protocol implementation: Parses an [RespHello] message and produces an
|
/// Core cryptographic protocol implementation: Parses an [RespHello] message and produces an
|
||||||
/// [InitConf] message on the initiator side.
|
/// [InitConf] message on the initiator side.
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "trace_bench",
|
||||||
|
rosenpass_util::trace_bench::trace_span("handle_resp_hello", rosenpass_util::trace_bench::TRACE)
|
||||||
|
)]
|
||||||
pub fn handle_resp_hello(&mut self, rh: &RespHello, ic: &mut InitConf) -> Result<PeerPtr> {
|
pub fn handle_resp_hello(&mut self, rh: &RespHello, ic: &mut InitConf) -> Result<PeerPtr> {
|
||||||
// RHI2
|
// RHI2
|
||||||
let peer = self
|
let peer = self
|
||||||
@@ -3700,24 +3763,34 @@ impl CryptoServer {
|
|||||||
// to save us from the repetitive secret unwrapping
|
// to save us from the repetitive secret unwrapping
|
||||||
|
|
||||||
// RHI3
|
// RHI3
|
||||||
core.mix(&rh.sidr)?.mix(&rh.sidi)?;
|
protocol_section!("RHI3", {
|
||||||
|
core.mix(&rh.sidr)?.mix(&rh.sidi)?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHI4
|
// RHI4
|
||||||
core.decaps_and_mix(
|
protocol_section!("RHI4", {
|
||||||
&EphemeralKem,
|
core.decaps_and_mix(
|
||||||
hs!().eski.secret(),
|
&EphemeralKem,
|
||||||
hs!().epki.deref(),
|
hs!().eski.secret(),
|
||||||
&rh.ecti,
|
hs!().epki.deref(),
|
||||||
)?;
|
&rh.ecti,
|
||||||
|
)?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHI5
|
// RHI5
|
||||||
core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &rh.scti)?;
|
protocol_section!("RHI5", {
|
||||||
|
core.decaps_and_mix(&StaticKem, self.sskm.secret(), self.spkm.deref(), &rh.scti)?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHI6
|
// RHI6
|
||||||
core.mix(&rh.biscuit)?;
|
protocol_section!("RHI6", {
|
||||||
|
core.mix(&rh.biscuit)?;
|
||||||
|
});
|
||||||
|
|
||||||
// RHI7
|
// RHI7
|
||||||
core.decrypt_and_mix(&mut [0u8; 0], &rh.auth)?;
|
protocol_section!("RHI7", {
|
||||||
|
core.decrypt_and_mix(&mut [0u8; 0], &rh.auth)?;
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: We should just authenticate the entire network package up to the auth
|
// TODO: We should just authenticate the entire network package up to the auth
|
||||||
// tag as a pattern instead of mixing in fields separately
|
// tag as a pattern instead of mixing in fields separately
|
||||||
@@ -3726,27 +3799,33 @@ impl CryptoServer {
|
|||||||
ic.sidr.copy_from_slice(&rh.sidr);
|
ic.sidr.copy_from_slice(&rh.sidr);
|
||||||
|
|
||||||
// ICI3
|
// ICI3
|
||||||
core.mix(&ic.sidi)?.mix(&ic.sidr)?;
|
protocol_section!("ICI3", {
|
||||||
ic.biscuit.copy_from_slice(&rh.biscuit);
|
core.mix(&ic.sidi)?.mix(&ic.sidr)?;
|
||||||
|
ic.biscuit.copy_from_slice(&rh.biscuit);
|
||||||
|
});
|
||||||
|
|
||||||
// ICI4
|
// ICI4
|
||||||
core.encrypt_and_mix(&mut ic.auth, &[])?;
|
protocol_section!("ICI4", {
|
||||||
|
core.encrypt_and_mix(&mut ic.auth, &[])?;
|
||||||
|
});
|
||||||
|
|
||||||
// Split() – We move the secrets into the session; we do not
|
// Split() – We move the secrets into the session; we do not
|
||||||
// delete the InitiatorHandshake, just clear it's secrets because
|
// delete the InitiatorHandshake, just clear it's secrets because
|
||||||
// we still need it for InitConf message retransmission to function.
|
// we still need it for InitConf message retransmission to function.
|
||||||
|
|
||||||
// ICI7
|
// ICI7
|
||||||
peer.session().insert(
|
protocol_section!("ICI7", {
|
||||||
self,
|
peer.session().insert(
|
||||||
core.enter_live(
|
|
||||||
self,
|
self,
|
||||||
HandshakeRole::Initiator,
|
core.enter_live(
|
||||||
peer.get(self).protocol_version.keyed_hash(),
|
self,
|
||||||
)?,
|
HandshakeRole::Initiator,
|
||||||
)?;
|
peer.get(self).protocol_version.keyed_hash(),
|
||||||
hs_mut!().core.erase();
|
)?,
|
||||||
hs_mut!().next = HandshakeStateMachine::RespConf;
|
)?;
|
||||||
|
hs_mut!().core.erase();
|
||||||
|
hs_mut!().next = HandshakeStateMachine::RespConf;
|
||||||
|
});
|
||||||
|
|
||||||
Ok(peer)
|
Ok(peer)
|
||||||
}
|
}
|
||||||
@@ -3756,6 +3835,10 @@ impl CryptoServer {
|
|||||||
///
|
///
|
||||||
/// This concludes the handshake on the cryptographic level; the [EmptyData] message is just
|
/// This concludes the handshake on the cryptographic level; the [EmptyData] message is just
|
||||||
/// an acknowledgement message telling the initiator to stop performing retransmissions.
|
/// an acknowledgement message telling the initiator to stop performing retransmissions.
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "trace_bench",
|
||||||
|
rosenpass_util::trace_bench::trace_span("handle_init_conf", rosenpass_util::trace_bench::TRACE)
|
||||||
|
)]
|
||||||
pub fn handle_init_conf(
|
pub fn handle_init_conf(
|
||||||
&mut self,
|
&mut self,
|
||||||
ic: &InitConf,
|
ic: &InitConf,
|
||||||
@@ -3764,22 +3847,30 @@ impl CryptoServer {
|
|||||||
) -> Result<PeerPtr> {
|
) -> Result<PeerPtr> {
|
||||||
// (peer, bn) ← LoadBiscuit(InitConf.biscuit)
|
// (peer, bn) ← LoadBiscuit(InitConf.biscuit)
|
||||||
// ICR1
|
// ICR1
|
||||||
let (peer, biscuit_no, mut core) = HandshakeState::load_biscuit(
|
let (peer, biscuit_no, mut core) = protocol_section!("ICR1", {
|
||||||
self,
|
HandshakeState::load_biscuit(
|
||||||
&ic.biscuit,
|
self,
|
||||||
SessionId::from_slice(&ic.sidi),
|
&ic.biscuit,
|
||||||
SessionId::from_slice(&ic.sidr),
|
SessionId::from_slice(&ic.sidi),
|
||||||
keyed_hash,
|
SessionId::from_slice(&ic.sidr),
|
||||||
)?;
|
keyed_hash,
|
||||||
|
)?
|
||||||
|
});
|
||||||
|
|
||||||
// ICR2
|
// ICR2
|
||||||
core.encrypt_and_mix(&mut [0u8; Aead::TAG_LEN], &[])?;
|
protocol_section!("ICR2", {
|
||||||
|
core.encrypt_and_mix(&mut [0u8; Aead::TAG_LEN], &[])?;
|
||||||
|
});
|
||||||
|
|
||||||
// ICR3
|
// ICR3
|
||||||
core.mix(&ic.sidi)?.mix(&ic.sidr)?;
|
protocol_section!("ICR3", {
|
||||||
|
core.mix(&ic.sidi)?.mix(&ic.sidr)?;
|
||||||
|
});
|
||||||
|
|
||||||
// ICR4
|
// ICR4
|
||||||
core.decrypt_and_mix(&mut [0u8; 0], &ic.auth)?;
|
protocol_section!("ICR4", {
|
||||||
|
core.decrypt_and_mix(&mut [0u8; 0], &ic.auth)?;
|
||||||
|
});
|
||||||
|
|
||||||
// ICR5
|
// ICR5
|
||||||
// Defense against replay attacks; implementations may accept
|
// Defense against replay attacks; implementations may accept
|
||||||
@@ -3791,20 +3882,24 @@ impl CryptoServer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// ICR6
|
// ICR6
|
||||||
peer.get_mut(self).biscuit_used = biscuit_no;
|
protocol_section!("ICR6", {
|
||||||
|
peer.get_mut(self).biscuit_used = biscuit_no;
|
||||||
|
});
|
||||||
|
|
||||||
// ICR7
|
// ICR7
|
||||||
peer.session().insert(
|
protocol_section!("ICR7", {
|
||||||
self,
|
peer.session().insert(
|
||||||
core.enter_live(
|
|
||||||
self,
|
self,
|
||||||
HandshakeRole::Responder,
|
core.enter_live(
|
||||||
peer.get(self).protocol_version.keyed_hash(),
|
self,
|
||||||
)?,
|
HandshakeRole::Responder,
|
||||||
)?;
|
peer.get(self).protocol_version.keyed_hash(),
|
||||||
// TODO: This should be part of the protocol specification.
|
)?,
|
||||||
// Abort any ongoing handshake from initiator role
|
)?;
|
||||||
peer.hs().take(self);
|
// TODO: This should be part of the protocol specification.
|
||||||
|
// Abort any ongoing handshake from initiator role
|
||||||
|
peer.hs().take(self);
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Implementing RP should be possible without touching the live session stuff
|
// TODO: Implementing RP should be possible without touching the live session stuff
|
||||||
// TODO: I fear that this may lead to race conditions; the acknowledgement may be
|
// TODO: I fear that this may lead to race conditions; the acknowledgement may be
|
||||||
@@ -3849,6 +3944,10 @@ impl CryptoServer {
|
|||||||
/// message then terminates the handshake.
|
/// message then terminates the handshake.
|
||||||
///
|
///
|
||||||
/// The EmptyData message is just there to tell the initiator to abort retransmissions.
|
/// The EmptyData message is just there to tell the initiator to abort retransmissions.
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "trace_bench",
|
||||||
|
rosenpass_util::trace_bench::trace_span("handle_resp_conf", rosenpass_util::trace_bench::TRACE)
|
||||||
|
)]
|
||||||
pub fn handle_resp_conf(
|
pub fn handle_resp_conf(
|
||||||
&mut self,
|
&mut self,
|
||||||
msg_in: &Ref<&[u8], Envelope<EmptyData>>,
|
msg_in: &Ref<&[u8], Envelope<EmptyData>>,
|
||||||
@@ -3906,6 +4005,10 @@ impl CryptoServer {
|
|||||||
/// DOS mitigation features.
|
/// DOS mitigation features.
|
||||||
///
|
///
|
||||||
/// See more on DOS mitigation in Rosenpass in the [whitepaper](https://rosenpass.eu/whitepaper.pdf).
|
/// See more on DOS mitigation in Rosenpass in the [whitepaper](https://rosenpass.eu/whitepaper.pdf).
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "trace_bench",
|
||||||
|
rosenpass_util::trace_bench::trace_span("handle_cookie_reply", rosenpass_util::trace_bench::TRACE)
|
||||||
|
)]
|
||||||
pub fn handle_cookie_reply(&mut self, cr: &CookieReply) -> Result<PeerPtr> {
|
pub fn handle_cookie_reply(&mut self, cr: &CookieReply) -> Result<PeerPtr> {
|
||||||
let peer_ptr: Option<PeerPtr> = self
|
let peer_ptr: Option<PeerPtr> = self
|
||||||
.lookup_session(Public::new(cr.inner.sid))
|
.lookup_session(Public::new(cr.inner.sid))
|
||||||
@@ -4030,7 +4133,7 @@ pub mod testutils {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::{borrow::BorrowMut, net::SocketAddrV4, ops::DerefMut, thread::sleep, time::Duration};
|
use std::{borrow::BorrowMut, net::SocketAddrV4, ops::DerefMut};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ thiserror = { workspace = true }
|
|||||||
mio = { workspace = true }
|
mio = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
uds = { workspace = true, optional = true, features = ["mio_1xx"] }
|
uds = { workspace = true, optional = true, features = ["mio_1xx"] }
|
||||||
|
libcrux-test-utils = { workspace = true, optional = true }
|
||||||
|
lazy_static = { workspace = true, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
experiment_file_descriptor_passing = ["uds"]
|
experiment_file_descriptor_passing = ["uds"]
|
||||||
|
trace_bench = ["dep:libcrux-test-utils", "dep:lazy_static"]
|
||||||
|
|||||||
@@ -36,3 +36,6 @@ pub mod typenum;
|
|||||||
pub mod zerocopy;
|
pub mod zerocopy;
|
||||||
/// Memory wiping utilities.
|
/// Memory wiping utilities.
|
||||||
pub mod zeroize;
|
pub mod zeroize;
|
||||||
|
/// Trace benchmarking utilities
|
||||||
|
#[cfg(feature = "trace_bench")]
|
||||||
|
pub mod trace_bench;
|
||||||
|
|||||||
19
util/src/trace_bench.rs
Normal file
19
util/src/trace_bench.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use libcrux_test_utils::tracing;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
/// The trace value used in all Rosepass crates.
|
||||||
|
pub static ref TRACE: RpTrace = RpTrace::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The trace type used to trace Rosenpass for performance measurement.
|
||||||
|
pub type RpTrace = tracing::MutexTrace<&'static str, Instant>;
|
||||||
|
|
||||||
|
/// The trace event type used to trace Rosenpass for performance measurement.
|
||||||
|
pub type RpEventType = tracing::TraceEvent<&'static str, Instant>;
|
||||||
|
|
||||||
|
// Re-export to make functionality availalable and callers don't need to also directly depend on
|
||||||
|
// [`libcrux_test_utils`].
|
||||||
|
pub use libcrux_test_utils::tracing::trace_span;
|
||||||
|
pub use tracing::Trace;
|
||||||
Reference in New Issue
Block a user