mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-18 21:34:37 +03:00
Compare commits
7 Commits
analyze_py
...
dev/karo/t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19cd94ee84 | ||
|
|
a6575b0b3b | ||
|
|
9ad0c7f305 | ||
|
|
db62908f27 | ||
|
|
f008cff089 | ||
|
|
38e04a2e03 | ||
|
|
36dcd0edd2 |
103
.github/workflows/bench-primitives.yml
vendored
Normal file
103
.github/workflows/bench-primitives.yml
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
name: rosenpass-ciphers - primitives - benchmark
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
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: 🛠️ 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 }}.benchmarks" --command cargo bench -F bench --bench primitives --verbose -- --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-os.json
|
||||||
|
|
||||||
|
- name: Fix up 'os' label in benchmark data
|
||||||
|
run: jq 'map(with_entries(.key |= if . == "os" then "operating system" else . end))' <bench-primitives-os.json >bench-primitives.json
|
||||||
|
|
||||||
|
- name: Upload benchmarks
|
||||||
|
uses: cryspen/benchmark-upload-and-plot-action@v3
|
||||||
|
with:
|
||||||
|
name: Crypto Primitives Benchmarks
|
||||||
|
group-by: "operating system,primitive,algorithm"
|
||||||
|
schema: "operating system,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 }}
|
||||||
|
auto-push: true
|
||||||
|
fail-on-alert: true
|
||||||
|
base-path: benchmarks/
|
||||||
|
|
||||||
|
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
|
||||||
90
.github/workflows/bench-protocol.yml
vendored
Normal file
90
.github/workflows/bench-protocol.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
name: rosenpass - protocol - benchmark
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
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: 🛠️ 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 }}.benchmarks" --command cargo bench -p rosenpass --bench trace_handshake -F trace_bench --verbose >bench-protocol.json
|
||||||
|
|
||||||
|
- name: Upload benchmarks
|
||||||
|
uses: cryspen/benchmark-upload-and-plot-action@v3
|
||||||
|
with:
|
||||||
|
name: Protocol Benchmarks
|
||||||
|
group-by: "operating system,architecture,protocol version,run time"
|
||||||
|
schema: "operating system,architecture,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 }}
|
||||||
|
auto-push: true
|
||||||
|
fail-on-alert: true
|
||||||
|
base-path: benchmarks/
|
||||||
|
|
||||||
|
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 }
|
||||||
|
|||||||
344
ciphers/benches/primitives.rs
Normal file
344
ciphers/benches/primitives.rs
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
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();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -89,6 +89,7 @@
|
|||||||
[
|
[
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
"aarch64-linux"
|
"aarch64-linux"
|
||||||
|
"i686-linux"
|
||||||
]
|
]
|
||||||
(
|
(
|
||||||
system:
|
system:
|
||||||
@@ -172,6 +173,14 @@
|
|||||||
inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA;
|
inherit (pkgs.cargo-llvm-cov) LLVM_COV LLVM_PROFDATA;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
devShells.benchmarks = pkgs.mkShell {
|
||||||
|
inputsFrom = [ pkgs.rosenpass ];
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
cargo-release
|
||||||
|
clippy
|
||||||
|
rustfmt
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
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=";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
47
readme.md
47
readme.md
@@ -14,7 +14,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 +64,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
|
||||||
|
|
||||||
@@ -87,6 +83,45 @@ Rosenpass is also available as prebuilt Docker images:
|
|||||||
|
|
||||||
For details on how to use these images, refer to the [Docker usage guide](docker/USAGE.md).
|
For details on how to use these images, refer to the [Docker usage guide](docker/USAGE.md).
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
This repository contains facilities for benchmarking both the Rosenpass
|
||||||
|
protocol code and the implementations of the cryptographic primitives used
|
||||||
|
by it. The primitives are benchmarked using criterion. For the protocol code
|
||||||
|
benchmarks we use a library for instrumenting the code such that events are
|
||||||
|
written to a trace, which is then inspected after a run.
|
||||||
|
|
||||||
|
Benchmarks are automatically run on CI. The measurements are visualized in the
|
||||||
|
[Benchmark Dashboard].
|
||||||
|
|
||||||
|
[Benchmark Dashboard]: https://rosenpass.github.io/benchmarks
|
||||||
|
|
||||||
|
### Primitive Benchmarks
|
||||||
|
|
||||||
|
There are benchmarks for the functions of the traits `Kem`, `Aead` and
|
||||||
|
`KeyedHash`. They 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.
|
||||||
|
|
||||||
|
### Protocol Benchmarks
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Mirrors
|
# Mirrors
|
||||||
|
|
||||||
Don't want to use GitHub or only have an IPv6 connection? Rosenpass has set up two mirrors for this:
|
Don't want to use GitHub or only have an IPv6 connection? Rosenpass has set up two mirrors for this:
|
||||||
|
|||||||
@@ -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 1ms",
|
||||||
|
RunTimeGroup::BelowMicrosec => "below 1us",
|
||||||
|
};
|
||||||
|
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}", "operating system":"{os}", "architecture":"{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,30 @@ 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 +3577,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 +3633,13 @@ 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 +3651,83 @@ 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 +3772,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 +3808,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 +3844,13 @@ 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 +3859,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 +3894,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 +3956,13 @@ 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 +4020,13 @@ 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 +4151,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"]
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ pub mod option;
|
|||||||
pub mod result;
|
pub mod result;
|
||||||
/// Time and duration utilities.
|
/// Time and duration utilities.
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
/// Trace benchmarking utilities
|
||||||
|
#[cfg(feature = "trace_bench")]
|
||||||
|
pub mod trace_bench;
|
||||||
/// Type-level numbers and arithmetic.
|
/// Type-level numbers and arithmetic.
|
||||||
pub mod typenum;
|
pub mod typenum;
|
||||||
/// Zero-copy serialization utilities.
|
/// Zero-copy serialization utilities.
|
||||||
|
|||||||
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