mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-18 13:24:38 +03:00
Compare commits
62 Commits
macos-runn
...
dev/karo/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fd3df67ed | ||
|
|
6d47169a5c | ||
|
|
4bcd38a4ea | ||
|
|
730a03957a | ||
|
|
ea071f5363 | ||
|
|
3063d3e4c2 | ||
|
|
1bf0eed90a | ||
|
|
138e6b6553 | ||
|
|
2dde0a2b47 | ||
|
|
3cc3b6009f | ||
|
|
1ab457ed37 | ||
|
|
c9c266fe7c | ||
|
|
8d3c8790fe | ||
|
|
648a94ead8 | ||
|
|
54ac5eecdb | ||
|
|
40c5bbd167 | ||
|
|
a4b8fc2226 | ||
|
|
37f7b3e4e9 | ||
|
|
deafc1c1af | ||
|
|
6bbe85a57b | ||
|
|
e70c5b33a8 | ||
|
|
25fdfef4d0 | ||
|
|
6ab8fafe59 | ||
|
|
c1aacf76b8 | ||
|
|
1bcaf5781f | ||
|
|
de60e5f8f0 | ||
|
|
b50ddda151 | ||
|
|
7282fba3b3 | ||
|
|
0cca389f10 | ||
|
|
8a08d49215 | ||
|
|
8637bc7884 | ||
|
|
4412c2bdd1 | ||
|
|
ecc815dd8e | ||
|
|
b7d7c03e35 | ||
|
|
f6320c3c35 | ||
|
|
19f7905bc9 | ||
|
|
9b5b7ee620 | ||
|
|
4fdd271de7 | ||
|
|
860e65965a | ||
|
|
87144233da | ||
|
|
d0a6e99a1f | ||
|
|
79b634fadf | ||
|
|
99ac3c0902 | ||
|
|
010c14dadf | ||
|
|
45b6132312 | ||
|
|
77f9fd38f3 | ||
|
|
775ed86adc | ||
|
|
40377dce1f | ||
|
|
19293471e8 | ||
|
|
cc5877dd83 | ||
|
|
ebb591aa6f | ||
|
|
07146d9914 | ||
|
|
cd04dbc4eb | ||
|
|
cc22165dc4 | ||
|
|
8496571765 | ||
|
|
ee3a1f580e | ||
|
|
89584645c3 | ||
|
|
3286e49370 | ||
|
|
100d7b6e1c | ||
|
|
921b2bfc39 | ||
|
|
a18658847c | ||
|
|
bdad414c90 |
33
.ci/run-regression.sh
Executable file
33
.ci/run-regression.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
iterations="$1"
|
||||||
|
sleep_time="$2"
|
||||||
|
|
||||||
|
PWD="$(pwd)"
|
||||||
|
EXEC="$PWD/target/release/rosenpass"
|
||||||
|
LOGS="$PWD/output/logs"
|
||||||
|
|
||||||
|
mkdir -p "$LOGS"
|
||||||
|
|
||||||
|
run_command() {
|
||||||
|
local file=$1
|
||||||
|
local log_file="$2"
|
||||||
|
("$EXEC" exchange-config "$file" 2>&1 | tee -a "$log_file") &
|
||||||
|
echo $!
|
||||||
|
}
|
||||||
|
|
||||||
|
pids=()
|
||||||
|
|
||||||
|
(cd output/dut && run_command "configs/dut-$iterations.toml" "$LOGS/dut.log")
|
||||||
|
for (( x=0; x<iterations; x++ )); do
|
||||||
|
(cd output/ate && run_command "configs/ate-$x.toml" "$LOGS/ate-$x.log") & pids+=($!)
|
||||||
|
done
|
||||||
|
|
||||||
|
sleep "$sleep_time"
|
||||||
|
|
||||||
|
lsof -i :9999 | awk 'NR!=1 {print $2}' | xargs kill
|
||||||
|
|
||||||
|
for (( x=0; x<iterations; x++ )); do
|
||||||
|
port=$((x + 50000))
|
||||||
|
lsof -i :$port | awk 'NR!=1 {print $2}' | xargs kill
|
||||||
|
done
|
||||||
21
.github/workflows/regressions.yml
vendored
Normal file
21
.github/workflows/regressions.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: QC
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
multi-peer:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- run: cargo build --bin rosenpass --release
|
||||||
|
- run: python misc/generate_configs.py
|
||||||
|
- run: chmod +x .ci/run-regression.sh
|
||||||
|
- run: .ci/run-regression.sh 100 20
|
||||||
|
- run: |
|
||||||
|
[ $(ls -1 output/ate/out | wc -l) -eq 100 ]
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -20,3 +20,8 @@ _markdown_*
|
|||||||
**/result
|
**/result
|
||||||
**/result-*
|
**/result-*
|
||||||
.direnv
|
.direnv
|
||||||
|
|
||||||
|
# Visual studio code
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
/output
|
||||||
|
|||||||
38
CONTRIBUTING.md
Normal file
38
CONTRIBUTING.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
**Making a new Release of Rosenpass — Cooking Recipe**
|
||||||
|
|
||||||
|
If you have to change a file, do what it takes to get the change as commit on the main branch, then **start from step 0**.
|
||||||
|
If any other issue occurs
|
||||||
|
|
||||||
|
0. Make sure you are in the root directory of the project
|
||||||
|
- `cd "$(git rev-parse --show-toplevel)"`
|
||||||
|
1. Make sure you locally checked out the head of the main branch
|
||||||
|
- `git stash --include-untracked && git checkout main && git pull`
|
||||||
|
2. Make sure all tests pass
|
||||||
|
- `cargo test`
|
||||||
|
3. Make sure the current version in `rosenpass/Cargo.toml` matches that in the [last release on GitHub](https://github.com/rosenpass/rosenpass/releases)
|
||||||
|
- Only normal releases count, release candidates and draft releases can be ignored
|
||||||
|
4. Pick the kind of release that you want to make (`major`, `minor`, `patch`, `rc`, ...)
|
||||||
|
- See `cargo release --help` for more information on the available release types
|
||||||
|
- Pick `rc` if in doubt
|
||||||
|
5. Try to release a new version
|
||||||
|
- `cargo release rc --package rosenpass`
|
||||||
|
- An issue was reported? Go fix it, start again with step 0!
|
||||||
|
6. Actually make the release
|
||||||
|
- `cargo release rc --package rosenpass --execute`
|
||||||
|
- Tentatively wait for any interactions, such as entering ssh keys etc.
|
||||||
|
- You may be asked for your ssh key multiple times!
|
||||||
|
|
||||||
|
**Frequently Asked Questions (FAQ)**
|
||||||
|
|
||||||
|
- You have untracked files, which `cargo release` complains about?
|
||||||
|
- `git stash --include-untracked`
|
||||||
|
- You cannot push to crates.io because you are not logged in?
|
||||||
|
- Follow the steps displayed in [`cargo login`](https://doc.rust-lang.org/cargo/commands/cargo-login.html)
|
||||||
|
- How is the release page added to [GitHub Releases](https://github.com/rosenpass/rosenpass/releases) itself?
|
||||||
|
- Our CI Pipeline will create the release, once `cargo release` pushed the new version tag to the repo. The new release should pop up almost immediately in [GitHub Releases](https://github.com/rosenpass/rosenpass/releases) after the [Actions/Release](https://github.com/rosenpass/rosenpass/actions/workflows/release.yaml) pipeline started.
|
||||||
|
- No new release pops up in the `Release` sidebar element on the [main page](https://github.com/rosenpass/rosenpass)
|
||||||
|
- Did you push a `rc` release? This view only shows non-draft release, but `rc` releases are considered as draft. See [Releases](https://github.com/rosenpass/rosenpass/releases) page to see all (including draft!) releases.
|
||||||
|
- The release page was created on GitHub, but there are no assets/artifacts other than the source code tar ball/zip?
|
||||||
|
- The artifacts are generated and pushed automatically to the release, but this takes some time (a couple of minutes). You can check the respective CI pipeline: [Actions/Release](https://github.com/rosenpass/rosenpass/actions/workflows/release.yaml), which should start immediately after `cargo release` pushed the new release tag to the repo. The release artifacts only are added later to the release, once all jobs in bespoke pipeline finished.
|
||||||
|
- How are the release artifacts generated, and what are they?
|
||||||
|
- The release artifacts are built using one Nix derivation per platform, `nix build .#release-package`. It contains both statically linked versions of `rosenpass` itself and OCI container images.
|
||||||
203
Cargo.lock
generated
203
Cargo.lock
generated
@@ -387,9 +387,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.7"
|
version = "4.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -397,9 +397,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.7"
|
version = "4.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -409,9 +409,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.5"
|
version = "4.5.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -984,8 +984,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1072,6 +1074,12 @@ version = "0.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex-literal"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
@@ -1228,6 +1236,38 @@ version = "0.2.155"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libcrux"
|
||||||
|
version = "0.0.2-pre.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "31d9dcd435758db03438089760c55a45e6bcab7e4e299ee261f75225ab29d482"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.15",
|
||||||
|
"libcrux-hacl",
|
||||||
|
"libcrux-platform",
|
||||||
|
"libjade-sys",
|
||||||
|
"rand 0.8.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libcrux-hacl"
|
||||||
|
version = "0.0.2-pre.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52b2581ce493c5c22700077b5552b47be69b67b8176716572b02856218db0b68"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libcrux-platform",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libcrux-platform"
|
||||||
|
version = "0.0.2-pre.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "647e39666194b11df17c19451d1154b9be79df98b9821532560c2ecad0cf3410"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libfuzzer-sys"
|
name = "libfuzzer-sys"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@@ -1239,6 +1279,16 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libjade-sys"
|
||||||
|
version = "0.0.2-pre.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec4d22bba476bf8f5aebe36ccfd0e56dba8707e0c3b5c76996576028f48ffb8e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libcrux-platform",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
@@ -1267,9 +1317,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.21"
|
version = "0.4.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
@@ -1341,14 +1391,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.11"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.9",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1533,16 +1584,6 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_cpus"
|
|
||||||
version = "1.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi 0.3.9",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.35.0"
|
version = "0.35.0"
|
||||||
@@ -1891,14 +1932,17 @@ name = "rosenpass"
|
|||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap 4.5.7",
|
"clap 4.5.13",
|
||||||
"criterion",
|
"criterion",
|
||||||
"derive_builder 0.20.0",
|
"derive_builder 0.20.0",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"heck",
|
||||||
|
"hex",
|
||||||
|
"hex-literal",
|
||||||
"home",
|
"home",
|
||||||
"log",
|
"log",
|
||||||
"memoffset 0.9.1",
|
"memoffset 0.9.1",
|
||||||
"mio 0.8.11",
|
"mio 1.0.1",
|
||||||
"paste",
|
"paste",
|
||||||
"procspawn",
|
"procspawn",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@@ -1913,10 +1957,12 @@ dependencies = [
|
|||||||
"serial_test",
|
"serial_test",
|
||||||
"stacker",
|
"stacker",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
|
"tempfile",
|
||||||
"test_bin",
|
"test_bin",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml",
|
"toml",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1930,6 +1976,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"blake2",
|
"blake2",
|
||||||
"chacha20poly1305",
|
"chacha20poly1305",
|
||||||
|
"libcrux",
|
||||||
"rosenpass-constant-time",
|
"rosenpass-constant-time",
|
||||||
"rosenpass-oqs",
|
"rosenpass-oqs",
|
||||||
"rosenpass-secret-memory",
|
"rosenpass-secret-memory",
|
||||||
@@ -2003,9 +2050,12 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64ct",
|
"base64ct",
|
||||||
|
"mio 1.0.1",
|
||||||
"rustix",
|
"rustix",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
|
"thiserror",
|
||||||
"typenum",
|
"typenum",
|
||||||
|
"zerocopy",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2014,11 +2064,11 @@ name = "rosenpass-wireguard-broker"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap 4.5.7",
|
"clap 4.5.13",
|
||||||
"derive_builder 0.20.0",
|
"derive_builder 0.20.0",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"mio 0.8.11",
|
"mio 1.0.1",
|
||||||
"postcard",
|
"postcard",
|
||||||
"procspawn",
|
"procspawn",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
@@ -2154,18 +2204,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.203"
|
version = "1.0.204"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.203"
|
version = "1.0.204"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2372,18 +2422,18 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.61"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.61"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2402,28 +2452,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.38.0"
|
version = "1.39.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio 0.8.11",
|
"mio 1.0.1",
|
||||||
"num_cpus",
|
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.3.0"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2657,15 +2706,6 @@ dependencies = [
|
|||||||
"windows-targets 0.42.2",
|
"windows-targets 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -2690,21 +2730,6 @@ dependencies = [
|
|||||||
"windows_x86_64_msvc 0.42.2",
|
"windows_x86_64_msvc 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.48.5",
|
|
||||||
"windows_aarch64_msvc 0.48.5",
|
|
||||||
"windows_i686_gnu 0.48.5",
|
|
||||||
"windows_i686_msvc 0.48.5",
|
|
||||||
"windows_x86_64_gnu 0.48.5",
|
|
||||||
"windows_x86_64_gnullvm 0.48.5",
|
|
||||||
"windows_x86_64_msvc 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.5"
|
version = "0.52.5"
|
||||||
@@ -2727,12 +2752,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.5"
|
||||||
@@ -2745,12 +2764,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.5"
|
||||||
@@ -2763,12 +2776,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.5"
|
version = "0.52.5"
|
||||||
@@ -2787,12 +2794,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.5"
|
||||||
@@ -2805,12 +2806,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.5"
|
version = "0.52.5"
|
||||||
@@ -2823,12 +2818,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.5"
|
||||||
@@ -2841,12 +2830,6 @@ version = "0.42.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.48.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.5"
|
||||||
@@ -2900,9 +2883,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.34"
|
version = "0.7.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
@@ -2910,9 +2893,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.7.34"
|
version = "0.7.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
28
Cargo.toml
28
Cargo.toml
@@ -35,7 +35,7 @@ doc-comment = "0.3.3"
|
|||||||
base64ct = {version = "1.6.0", default-features=false}
|
base64ct = {version = "1.6.0", default-features=false}
|
||||||
zeroize = "1.8.1"
|
zeroize = "1.8.1"
|
||||||
memoffset = "0.9.1"
|
memoffset = "0.9.1"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.63"
|
||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
env_logger = "0.10.2"
|
env_logger = "0.10.2"
|
||||||
toml = "0.7.8"
|
toml = "0.7.8"
|
||||||
@@ -44,26 +44,30 @@ allocator-api2 = "0.2.14"
|
|||||||
memsec = { git="https://github.com/rosenpass/memsec.git" ,rev="aceb9baee8aec6844125bd6612f92e9a281373df", features = [ "alloc_ext", ] }
|
memsec = { git="https://github.com/rosenpass/memsec.git" ,rev="aceb9baee8aec6844125bd6612f92e9a281373df", features = [ "alloc_ext", ] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
typenum = "1.17.0"
|
typenum = "1.17.0"
|
||||||
log = { version = "0.4.21" }
|
log = { version = "0.4.22" }
|
||||||
clap = { version = "4.5.7", features = ["derive"] }
|
clap = { version = "4.5.13", features = ["derive"] }
|
||||||
serde = { version = "1.0.203", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
arbitrary = { version = "1.3.2", features = ["derive"] }
|
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||||
anyhow = { version = "1.0.86", features = ["backtrace", "std"] }
|
anyhow = { version = "1.0.86", features = ["backtrace", "std"] }
|
||||||
mio = { version = "0.8.11", features = ["net", "os-poll"] }
|
mio = { version = "1.0.1", features = ["net", "os-poll"] }
|
||||||
oqs-sys = { version = "0.9.1", default-features = false, features = [
|
oqs-sys = { version = "0.9.1", default-features = false, features = [
|
||||||
'classic_mceliece',
|
'classic_mceliece',
|
||||||
'kyber',
|
'kyber',
|
||||||
] }
|
] }
|
||||||
blake2 = "0.10.6"
|
blake2 = "0.10.6"
|
||||||
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
|
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"heapless",
|
"heapless",
|
||||||
] }
|
] }
|
||||||
zerocopy = { version = "0.7.34", features = ["derive"] }
|
zerocopy = { version = "0.7.35", features = ["derive"] }
|
||||||
home = "0.5.9"
|
home = "0.5.9"
|
||||||
derive_builder = "0.20.0"
|
derive_builder = "0.20.0"
|
||||||
tokio = { version = "1.38", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.39", features = ["macros", "rt-multi-thread"] }
|
||||||
postcard= {version = "1.0.8", features = ["alloc"]}
|
postcard= {version = "1.0.8", features = ["alloc"]}
|
||||||
|
libcrux = { version = "0.0.2-pre.2" }
|
||||||
|
hex-literal = { version = "0.4.1" }
|
||||||
|
hex = { version = "0.4.3" }
|
||||||
|
heck = { version = "0.5.0" }
|
||||||
|
|
||||||
#Dev dependencies
|
#Dev dependencies
|
||||||
serial_test = "3.1.1"
|
serial_test = "3.1.1"
|
||||||
@@ -79,4 +83,4 @@ procspawn = {version = "1.0.0", features= ["test-support"]}
|
|||||||
#Broker dependencies (might need cleanup or changes)
|
#Broker dependencies (might need cleanup or changes)
|
||||||
wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
|
wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
|
||||||
command-fds = "0.2.3"
|
command-fds = "0.2.3"
|
||||||
rustix = { version = "0.38.27", features = ["net"] }
|
rustix = { version = "0.38.27", features = ["net", "fs"] }
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
//! The [KEM] Trait describes the basic API offered by a Key Encapsulation
|
//! The [KEM] Trait describes the basic API offered by a Key Encapsulation
|
||||||
//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM].
|
//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM].
|
||||||
|
|
||||||
use std::result::Result;
|
|
||||||
|
|
||||||
/// Key Encapsulation Mechanism
|
/// Key Encapsulation Mechanism
|
||||||
///
|
///
|
||||||
/// The KEM interface defines three operations: Key generation, key encapsulation and key
|
/// The KEM interface defines three operations: Key generation, key encapsulation and key
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ homepage = "https://rosenpass.eu/"
|
|||||||
repository = "https://github.com/rosenpass/rosenpass"
|
repository = "https://github.com/rosenpass/rosenpass"
|
||||||
readme = "readme.md"
|
readme = "readme.md"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
experiment_libcrux = ["dep:libcrux"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
rosenpass-to = { workspace = true }
|
rosenpass-to = { workspace = true }
|
||||||
@@ -20,3 +23,4 @@ static_assertions = { workspace = true }
|
|||||||
zeroize = { workspace = true }
|
zeroize = { workspace = true }
|
||||||
chacha20poly1305 = { workspace = true }
|
chacha20poly1305 = { workspace = true }
|
||||||
blake2 = { workspace = true }
|
blake2 = { workspace = true }
|
||||||
|
libcrux = { workspace = true, optional = true }
|
||||||
|
|||||||
@@ -9,7 +9,12 @@ const_assert!(KEY_LEN == hash_domain::KEY_LEN);
|
|||||||
|
|
||||||
/// Authenticated encryption with associated data
|
/// Authenticated encryption with associated data
|
||||||
pub mod aead {
|
pub mod aead {
|
||||||
|
#[cfg(not(feature = "experiment_libcrux"))]
|
||||||
pub use crate::subtle::chacha20poly1305_ietf::{decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN};
|
pub use crate::subtle::chacha20poly1305_ietf::{decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN};
|
||||||
|
#[cfg(feature = "experiment_libcrux")]
|
||||||
|
pub use crate::subtle::chacha20poly1305_ietf_libcrux::{
|
||||||
|
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authenticated encryption with associated data with a constant nonce
|
/// Authenticated encryption with associated data with a constant nonce
|
||||||
|
|||||||
60
ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs
Normal file
60
ciphers/src/subtle/chacha20poly1305_ietf_libcrux.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
use rosenpass_to::ops::copy_slice;
|
||||||
|
use rosenpass_to::To;
|
||||||
|
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
pub const KEY_LEN: usize = 32; // Grrrr! Libcrux, please provide me these constants.
|
||||||
|
pub const TAG_LEN: usize = 16;
|
||||||
|
pub const NONCE_LEN: usize = 12;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn encrypt(
|
||||||
|
ciphertext: &mut [u8],
|
||||||
|
key: &[u8],
|
||||||
|
nonce: &[u8],
|
||||||
|
ad: &[u8],
|
||||||
|
plaintext: &[u8],
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let (ciphertext, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
|
||||||
|
|
||||||
|
use libcrux::aead as C;
|
||||||
|
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
|
||||||
|
let crux_iv = C::Iv(nonce.try_into().unwrap());
|
||||||
|
|
||||||
|
copy_slice(plaintext).to(ciphertext);
|
||||||
|
let crux_tag = libcrux::aead::encrypt(&crux_key, ciphertext, crux_iv, ad).unwrap();
|
||||||
|
copy_slice(crux_tag.as_ref()).to(mac);
|
||||||
|
|
||||||
|
match crux_key {
|
||||||
|
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn decrypt(
|
||||||
|
plaintext: &mut [u8],
|
||||||
|
key: &[u8],
|
||||||
|
nonce: &[u8],
|
||||||
|
ad: &[u8],
|
||||||
|
ciphertext: &[u8],
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let (ciphertext, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
|
||||||
|
|
||||||
|
use libcrux::aead as C;
|
||||||
|
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
|
||||||
|
let crux_iv = C::Iv(nonce.try_into().unwrap());
|
||||||
|
let crux_tag = C::Tag::from_slice(mac).unwrap();
|
||||||
|
|
||||||
|
copy_slice(ciphertext).to(plaintext);
|
||||||
|
libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag).unwrap();
|
||||||
|
|
||||||
|
match crux_key {
|
||||||
|
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
pub mod blake2b;
|
pub mod blake2b;
|
||||||
|
#[cfg(not(feature = "experiment_libcrux"))]
|
||||||
pub mod chacha20poly1305_ietf;
|
pub mod chacha20poly1305_ietf;
|
||||||
|
#[cfg(feature = "experiment_libcrux")]
|
||||||
|
pub mod chacha20poly1305_ietf_libcrux;
|
||||||
pub mod incorrect_hmac_blake2b;
|
pub mod incorrect_hmac_blake2b;
|
||||||
pub mod xchacha20poly1305_ietf;
|
pub mod xchacha20poly1305_ietf;
|
||||||
|
|||||||
@@ -7,18 +7,16 @@
|
|||||||
///
|
///
|
||||||
/// The execution time of the function grows approx. linear with the length of the input. This is
|
/// The execution time of the function grows approx. linear with the length of the input. This is
|
||||||
/// considered safe.
|
/// considered safe.
|
||||||
///
|
|
||||||
/// ## Tests
|
|
||||||
/// [`tests::memcmp_runs_in_constant_time`] runs a stasticial test that the equality of the two
|
|
||||||
/// input parameters does not correlate with the run time.
|
|
||||||
///
|
|
||||||
/// For discussion on how to (further) ensure the constant-time execution of this function,
|
|
||||||
/// see <https://github.com/rosenpass/rosenpass/issues/232>
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn memcmp(a: &[u8], b: &[u8]) -> bool {
|
pub fn memcmp(a: &[u8], b: &[u8]) -> bool {
|
||||||
a.len() == b.len() && unsafe { memsec::memeq(a.as_ptr(), b.as_ptr(), a.len()) }
|
a.len() == b.len() && unsafe { memsec::memeq(a.as_ptr(), b.as_ptr(), a.len()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [tests::memcmp_runs_in_constant_time] runs a stasticial test that the equality of the two
|
||||||
|
/// input parameters does not correlate with the run time.
|
||||||
|
///
|
||||||
|
/// For discussion on how to (further) ensure the constant-time execution of this function,
|
||||||
|
/// see <https://github.com/rosenpass/rosenpass/issues/232>
|
||||||
#[cfg(all(test, feature = "constant_time_tests"))]
|
#[cfg(all(test, feature = "constant_time_tests"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ Rosenpass was created by Karolin Varner, Benjamin Lipp, Wanja Zaeske,
|
|||||||
Marei Peischl, Stephan Ajuvo, and Lisa Schmidt.
|
Marei Peischl, Stephan Ajuvo, and Lisa Schmidt.
|
||||||
.Pp
|
.Pp
|
||||||
This manual page was written by
|
This manual page was written by
|
||||||
.An Emil Engler
|
.An Clara Engler
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
The bugs are tracked at
|
The bugs are tracked at
|
||||||
.Lk https://github.com/rosenpass/rosenpass/issues .
|
.Lk https://github.com/rosenpass/rosenpass/issues .
|
||||||
|
|||||||
2
doc/rp.1
2
doc/rp.1
@@ -113,7 +113,7 @@ Rosenpass was created by Karolin Varner, Benjamin Lipp, Wanja Zaeske,
|
|||||||
Marei Peischl, Stephan Ajuvo, and Lisa Schmidt.
|
Marei Peischl, Stephan Ajuvo, and Lisa Schmidt.
|
||||||
.Pp
|
.Pp
|
||||||
This manual page was written by
|
This manual page was written by
|
||||||
.An Emil Engler
|
.An Clara Engler
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
The bugs are tracked at
|
The bugs are tracked at
|
||||||
.Lk https://github.com/rosenpass/rosenpass/issues .
|
.Lk https://github.com/rosenpass/rosenpass/issues .
|
||||||
|
|||||||
@@ -411,12 +411,12 @@
|
|||||||
inherit (packages.proof-proverif) CRYPTOVERIF_LIB;
|
inherit (packages.proof-proverif) CRYPTOVERIF_LIB;
|
||||||
inputsFrom = [ packages.default ];
|
inputsFrom = [ packages.default ];
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
|
inputs.fenix.packages.${system}.complete.toolchain
|
||||||
cmake # override the fakecmake from the main step above
|
cmake # override the fakecmake from the main step above
|
||||||
cargo-release
|
cargo-release
|
||||||
clippy
|
clippy
|
||||||
nodePackages.prettier
|
nodePackages.prettier
|
||||||
nushell # for the .ci/gen-workflow-files.nu script
|
nushell # for the .ci/gen-workflow-files.nu script
|
||||||
rustfmt
|
|
||||||
packages.proverif-patched
|
packages.proverif-patched
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ version = "0.0.1"
|
|||||||
publish = false
|
publish = false
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
cargo-fuzz = true
|
cargo-fuzz = true
|
||||||
|
|
||||||
@@ -81,4 +84,4 @@ doc = false
|
|||||||
name = "fuzz_vec_secret_alloc_memfdsec_mallocfb"
|
name = "fuzz_vec_secret_alloc_memfdsec_mallocfb"
|
||||||
path = "fuzz_targets/vec_secret_alloc_memfdsec_mallocfb.rs"
|
path = "fuzz_targets/vec_secret_alloc_memfdsec_mallocfb.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ pub struct Input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fuzz_target!(|input: Input| {
|
fuzz_target!(|input: Input| {
|
||||||
let mut ciphertext: Vec<u8> = Vec::with_capacity(input.plaintext.len() + 16);
|
let mut ciphertext = vec![0u8; input.plaintext.len() + 16];
|
||||||
ciphertext.resize(input.plaintext.len() + 16, 0);
|
|
||||||
|
|
||||||
aead::encrypt(
|
aead::encrypt(
|
||||||
ciphertext.as_mut_slice(),
|
ciphertext.as_mut_slice(),
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ use rosenpass::protocol::CryptoServer;
|
|||||||
use rosenpass_cipher_traits::Kem;
|
use rosenpass_cipher_traits::Kem;
|
||||||
use rosenpass_ciphers::kem::StaticKem;
|
use rosenpass_ciphers::kem::StaticKem;
|
||||||
use rosenpass_secret_memory::policy::*;
|
use rosenpass_secret_memory::policy::*;
|
||||||
use rosenpass_secret_memory::Secret;
|
use rosenpass_secret_memory::{PublicBox, Secret};
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
static ONCE: Once = Once::new();
|
static ONCE: Once = Once::new();
|
||||||
fuzz_target!(|rx_buf: &[u8]| {
|
fuzz_target!(|rx_buf: &[u8]| {
|
||||||
ONCE.call_once(secret_policy_use_only_malloc_secrets);
|
ONCE.call_once(secret_policy_use_only_malloc_secrets);
|
||||||
let sk = Secret::from_slice(&[0; StaticKem::SK_LEN]);
|
let sk = Secret::from_slice(&[0; StaticKem::SK_LEN]);
|
||||||
let pk = Secret::from_slice(&[0; StaticKem::PK_LEN]);
|
let pk = PublicBox::from_slice(&[0; StaticKem::PK_LEN]);
|
||||||
|
|
||||||
let mut cs = CryptoServer::new(sk, pk);
|
let mut cs = CryptoServer::new(sk, pk);
|
||||||
let mut tx_buf = [0; 10240];
|
let mut tx_buf = [0; 10240];
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub struct Input {
|
|||||||
|
|
||||||
fuzz_target!(|input: Input| {
|
fuzz_target!(|input: Input| {
|
||||||
let mut ciphertext = [0u8; EphemeralKem::CT_LEN];
|
let mut ciphertext = [0u8; EphemeralKem::CT_LEN];
|
||||||
let mut shared_secret = [0u8; EphemeralKem::SK_LEN];
|
let mut shared_secret = [0u8; EphemeralKem::SHK_LEN];
|
||||||
|
|
||||||
EphemeralKem::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap();
|
EphemeralKem::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import run
|
from subprocess import run
|
||||||
|
import os
|
||||||
|
|
||||||
config = dict(
|
config = dict(
|
||||||
peer_counts=[1, 5, 10, 50, 100, 500],
|
peer_counts=[1, 5, 10, 50, 100, 500],
|
||||||
peer_count_max=100,
|
peer_count_max=100,
|
||||||
ate_ip="192.168.2.1",
|
ate_ip="127.0.0.1",
|
||||||
dut_ip="192.168.2.4",
|
dut_ip="127.0.0.1",
|
||||||
dut_port=9999,
|
dut_port=9999,
|
||||||
path_to_rosenpass_bin="/Users/user/src/rosenppass/rosenpass/target/debug/rosenpass",
|
path_to_rosenpass_bin=os.getcwd() + "/target/release/rosenpass",
|
||||||
)
|
)
|
||||||
|
|
||||||
print(config)
|
print(config)
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ A wrapper script provides instant feedback about which queries execute as expect
|
|||||||
|
|
||||||
# Getting Rosenpass
|
# Getting Rosenpass
|
||||||
|
|
||||||
|
Documentation and installation guides can be found at the [Rosenpass website](https://rosenpass.eu/docs).
|
||||||
|
|
||||||
Rosenpass is packaged for more and more distributions, maybe also for the distribution of your choice?
|
Rosenpass is packaged for more and more distributions, maybe also for the distribution of your choice?
|
||||||
|
|
||||||
[](https://repology.org/project/rosenpass/versions)
|
[](https://repology.org/project/rosenpass/versions)
|
||||||
|
|||||||
@@ -13,6 +13,15 @@ readme = "readme.md"
|
|||||||
name = "rosenpass"
|
name = "rosenpass"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "rosenpass-gen-ipc-msg-types"
|
||||||
|
path = "src/bin/gen-ipc-msg-types.rs"
|
||||||
|
required-features = ["experiment_api", "internal_bin_gen_ipc_msg_types"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "api-integration-tests"
|
||||||
|
required-features = ["experiment_api", "internal_testing"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "handshake"
|
name = "handshake"
|
||||||
harness = false
|
harness = false
|
||||||
@@ -40,6 +49,10 @@ zerocopy = { workspace = true }
|
|||||||
home = { workspace = true }
|
home = { workspace = true }
|
||||||
derive_builder = {workspace = true}
|
derive_builder = {workspace = true}
|
||||||
rosenpass-wireguard-broker = {workspace = true}
|
rosenpass-wireguard-broker = {workspace = true}
|
||||||
|
zeroize = { workspace = true }
|
||||||
|
hex-literal = { workspace = true, optional = true }
|
||||||
|
hex = { workspace = true, optional = true }
|
||||||
|
heck = { workspace = true, optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
@@ -50,7 +63,12 @@ test_bin = { workspace = true }
|
|||||||
stacker = { workspace = true }
|
stacker = { workspace = true }
|
||||||
serial_test = {workspace = true}
|
serial_test = {workspace = true}
|
||||||
procspawn = {workspace = true}
|
procspawn = {workspace = true}
|
||||||
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
enable_broker_api = ["rosenpass-wireguard-broker/enable_broker_api"]
|
enable_broker_api = ["rosenpass-wireguard-broker/enable_broker_api"]
|
||||||
enable_memfd_alloc = []
|
experiment_memfd_secret = []
|
||||||
|
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"]
|
||||||
|
experiment_api = ["hex-literal"]
|
||||||
|
internal_testing = []
|
||||||
|
internal_bin_gen_ipc_msg_types = ["hex", "heck"]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use rosenpass::protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, SPk, SSk, SymKey};
|
use rosenpass::protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, SPk, SSk, SymKey};
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
use rosenpass_cipher_traits::Kem;
|
use rosenpass_cipher_traits::Kem;
|
||||||
use rosenpass_ciphers::kem::StaticKem;
|
use rosenpass_ciphers::kem::StaticKem;
|
||||||
@@ -40,7 +41,7 @@ fn hs(ini: &mut CryptoServer, res: &mut CryptoServer) -> Result<()> {
|
|||||||
|
|
||||||
fn keygen() -> Result<(SSk, SPk)> {
|
fn keygen() -> Result<(SSk, SPk)> {
|
||||||
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||||
StaticKem::keygen(sk.secret_mut(), pk.secret_mut())?;
|
StaticKem::keygen(sk.secret_mut(), pk.deref_mut())?;
|
||||||
Ok((sk, pk))
|
Ok((sk, pk))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
116
rosenpass/src/api/boilerplate/byte_slice_ext.rs
Normal file
116
rosenpass/src/api/boilerplate/byte_slice_ext.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
use zerocopy::{ByteSlice, Ref};
|
||||||
|
|
||||||
|
use rosenpass_util::zerocopy::{RefMaker, ZerocopySliceExt};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
PingRequest, PingResponse, RawMsgType, RefMakerRawMsgTypeExt, RequestMsgType, RequestRef,
|
||||||
|
ResponseMsgType, ResponseRef,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait ByteSliceRefExt: ByteSlice {
|
||||||
|
fn msg_type_maker(self) -> RefMaker<Self, RawMsgType> {
|
||||||
|
self.zk_ref_maker()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msg_type(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||||
|
self.zk_parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msg_type_from_prefix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||||
|
self.zk_parse_prefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msg_type_from_suffix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||||
|
self.zk_parse_suffix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_msg_type(self) -> anyhow::Result<RequestMsgType> {
|
||||||
|
self.msg_type_maker().parse_request_msg_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_msg_type_from_prefix(self) -> anyhow::Result<RequestMsgType> {
|
||||||
|
self.msg_type_maker()
|
||||||
|
.from_prefix()?
|
||||||
|
.parse_request_msg_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_msg_type_from_suffix(self) -> anyhow::Result<RequestMsgType> {
|
||||||
|
self.msg_type_maker()
|
||||||
|
.from_suffix()?
|
||||||
|
.parse_request_msg_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn response_msg_type(self) -> anyhow::Result<ResponseMsgType> {
|
||||||
|
self.msg_type_maker().parse_response_msg_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn response_msg_type_from_prefix(self) -> anyhow::Result<ResponseMsgType> {
|
||||||
|
self.msg_type_maker()
|
||||||
|
.from_prefix()?
|
||||||
|
.parse_response_msg_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn response_msg_type_from_suffix(self) -> anyhow::Result<ResponseMsgType> {
|
||||||
|
self.msg_type_maker()
|
||||||
|
.from_suffix()?
|
||||||
|
.parse_response_msg_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_request(self) -> anyhow::Result<RequestRef<Self>> {
|
||||||
|
RequestRef::parse(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_request_from_prefix(self) -> anyhow::Result<RequestRef<Self>> {
|
||||||
|
RequestRef::parse_from_prefix(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_request_from_suffix(self) -> anyhow::Result<RequestRef<Self>> {
|
||||||
|
RequestRef::parse_from_suffix(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_response(self) -> anyhow::Result<ResponseRef<Self>> {
|
||||||
|
ResponseRef::parse(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_response_from_prefix(self) -> anyhow::Result<ResponseRef<Self>> {
|
||||||
|
ResponseRef::parse_from_prefix(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_response_from_suffix(self) -> anyhow::Result<ResponseRef<Self>> {
|
||||||
|
ResponseRef::parse_from_suffix(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ping_request_maker(self) -> RefMaker<Self, PingRequest> {
|
||||||
|
self.zk_ref_maker()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ping_request(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||||
|
self.zk_parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ping_request_from_prefix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||||
|
self.zk_parse_prefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ping_request_from_suffix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
|
||||||
|
self.zk_parse_suffix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ping_response_maker(self) -> RefMaker<Self, PingResponse> {
|
||||||
|
self.zk_ref_maker()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ping_response(self) -> anyhow::Result<Ref<Self, PingResponse>> {
|
||||||
|
self.zk_parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ping_response_from_prefix(self) -> anyhow::Result<Ref<Self, PingResponse>> {
|
||||||
|
self.zk_parse_prefix()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ping_response_from_suffix(self) -> anyhow::Result<Ref<Self, PingResponse>> {
|
||||||
|
self.zk_parse_suffix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSlice> ByteSliceRefExt for B {}
|
||||||
29
rosenpass/src/api/boilerplate/message_trait.rs
Normal file
29
rosenpass/src/api/boilerplate/message_trait.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use zerocopy::{ByteSliceMut, Ref};
|
||||||
|
|
||||||
|
use rosenpass_util::zerocopy::RefMaker;
|
||||||
|
|
||||||
|
use super::RawMsgType;
|
||||||
|
|
||||||
|
pub trait Message {
|
||||||
|
type Payload;
|
||||||
|
type MessageClass: Into<RawMsgType>;
|
||||||
|
const MESSAGE_TYPE: Self::MessageClass;
|
||||||
|
|
||||||
|
fn from_payload(payload: Self::Payload) -> Self;
|
||||||
|
fn init(&mut self);
|
||||||
|
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ZerocopyResponseMakerSetupMessageExt<B, T> {
|
||||||
|
fn setup_msg(self) -> anyhow::Result<Ref<B, T>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, T> ZerocopyResponseMakerSetupMessageExt<B, T> for RefMaker<B, T>
|
||||||
|
where
|
||||||
|
B: ByteSliceMut,
|
||||||
|
T: Message,
|
||||||
|
{
|
||||||
|
fn setup_msg(self) -> anyhow::Result<Ref<B, T>> {
|
||||||
|
T::setup(self.into_buf())
|
||||||
|
}
|
||||||
|
}
|
||||||
117
rosenpass/src/api/boilerplate/message_type.rs
Normal file
117
rosenpass/src/api/boilerplate/message_type.rs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
use hex_literal::hex;
|
||||||
|
use rosenpass_util::zerocopy::RefMaker;
|
||||||
|
use zerocopy::ByteSlice;
|
||||||
|
|
||||||
|
use crate::RosenpassError::{self, InvalidApiMessageType};
|
||||||
|
|
||||||
|
pub type RawMsgType = u128;
|
||||||
|
|
||||||
|
// constants generated by gen-ipc-msg-types:
|
||||||
|
// hash domain hash of: Rosenpass IPC API -> Rosenpass Protocol Server -> Ping Request
|
||||||
|
pub const PING_REQUEST: RawMsgType =
|
||||||
|
RawMsgType::from_le_bytes(hex!("2397 3ecc c441 704d 0b02 ea31 45d3 4999"));
|
||||||
|
// hash domain hash of: Rosenpass IPC API -> Rosenpass Protocol Server -> Ping Response
|
||||||
|
pub const PING_RESPONSE: RawMsgType =
|
||||||
|
RawMsgType::from_le_bytes(hex!("4ec7 f6f0 2bbc ba64 48f1 da14 c7cf 0260"));
|
||||||
|
|
||||||
|
pub trait MessageAttributes {
|
||||||
|
fn message_size(&self) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
|
pub enum RequestMsgType {
|
||||||
|
Ping,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||||
|
pub enum ResponseMsgType {
|
||||||
|
Ping,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageAttributes for RequestMsgType {
|
||||||
|
fn message_size(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Ping => std::mem::size_of::<super::PingRequest>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageAttributes for ResponseMsgType {
|
||||||
|
fn message_size(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Ping => std::mem::size_of::<super::PingResponse>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawMsgType> for RequestMsgType {
|
||||||
|
type Error = RosenpassError;
|
||||||
|
|
||||||
|
fn try_from(value: RawMsgType) -> Result<Self, Self::Error> {
|
||||||
|
use RequestMsgType as E;
|
||||||
|
Ok(match value {
|
||||||
|
self::PING_REQUEST => E::Ping,
|
||||||
|
_ => return Err(InvalidApiMessageType(value)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RequestMsgType> for RawMsgType {
|
||||||
|
fn from(val: RequestMsgType) -> Self {
|
||||||
|
use RequestMsgType as E;
|
||||||
|
match val {
|
||||||
|
E::Ping => self::PING_REQUEST,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawMsgType> for ResponseMsgType {
|
||||||
|
type Error = RosenpassError;
|
||||||
|
|
||||||
|
fn try_from(value: RawMsgType) -> Result<Self, Self::Error> {
|
||||||
|
use ResponseMsgType as E;
|
||||||
|
Ok(match value {
|
||||||
|
self::PING_RESPONSE => E::Ping,
|
||||||
|
_ => return Err(InvalidApiMessageType(value)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ResponseMsgType> for RawMsgType {
|
||||||
|
fn from(val: ResponseMsgType) -> Self {
|
||||||
|
use ResponseMsgType as E;
|
||||||
|
match val {
|
||||||
|
E::Ping => self::PING_RESPONSE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RawMsgTypeExt {
|
||||||
|
fn into_request_msg_type(self) -> Result<RequestMsgType, RosenpassError>;
|
||||||
|
fn into_response_msg_type(self) -> Result<ResponseMsgType, RosenpassError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawMsgTypeExt for RawMsgType {
|
||||||
|
fn into_request_msg_type(self) -> Result<RequestMsgType, RosenpassError> {
|
||||||
|
self.try_into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_response_msg_type(self) -> Result<ResponseMsgType, RosenpassError> {
|
||||||
|
self.try_into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RefMakerRawMsgTypeExt {
|
||||||
|
fn parse_request_msg_type(self) -> anyhow::Result<RequestMsgType>;
|
||||||
|
fn parse_response_msg_type(self) -> anyhow::Result<ResponseMsgType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSlice> RefMakerRawMsgTypeExt for RefMaker<B, RawMsgType> {
|
||||||
|
fn parse_request_msg_type(self) -> anyhow::Result<RequestMsgType> {
|
||||||
|
Ok(self.parse()?.read().try_into()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_response_msg_type(self) -> anyhow::Result<ResponseMsgType> {
|
||||||
|
Ok(self.parse()?.read().try_into()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
17
rosenpass/src/api/boilerplate/mod.rs
Normal file
17
rosenpass/src/api/boilerplate/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
mod byte_slice_ext;
|
||||||
|
mod message_trait;
|
||||||
|
mod message_type;
|
||||||
|
mod payload;
|
||||||
|
mod request_ref;
|
||||||
|
mod request_response;
|
||||||
|
mod response_ref;
|
||||||
|
mod server;
|
||||||
|
|
||||||
|
pub use byte_slice_ext::*;
|
||||||
|
pub use message_trait::*;
|
||||||
|
pub use message_type::*;
|
||||||
|
pub use payload::*;
|
||||||
|
pub use request_ref::*;
|
||||||
|
pub use request_response::*;
|
||||||
|
pub use response_ref::*;
|
||||||
|
pub use server::*;
|
||||||
96
rosenpass/src/api/boilerplate/payload.rs
Normal file
96
rosenpass/src/api/boilerplate/payload.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use rosenpass_util::zerocopy::ZerocopyMutSliceExt;
|
||||||
|
use zerocopy::{AsBytes, ByteSliceMut, FromBytes, FromZeroes, Ref};
|
||||||
|
|
||||||
|
use super::{Message, RawMsgType, RequestMsgType, ResponseMsgType};
|
||||||
|
|
||||||
|
/// Size required to fit any message in binary form
|
||||||
|
pub const MAX_REQUEST_LEN: usize = 2500; // TODO fix this
|
||||||
|
pub const MAX_RESPONSE_LEN: usize = 2500; // TODO fix this
|
||||||
|
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
|
||||||
|
pub struct Envelope<M: AsBytes + FromBytes> {
|
||||||
|
/// Which message this is
|
||||||
|
pub msg_type: RawMsgType,
|
||||||
|
/// The actual Paylod
|
||||||
|
pub payload: M,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RequestEnvelope<M> = Envelope<M>;
|
||||||
|
pub type ResponseEnvelope<M> = Envelope<M>;
|
||||||
|
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
|
||||||
|
pub struct PingRequestPayload {
|
||||||
|
/// Randomly generated connection id
|
||||||
|
pub echo: [u8; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PingRequest = RequestEnvelope<PingRequestPayload>;
|
||||||
|
|
||||||
|
impl PingRequest {
|
||||||
|
pub fn new(echo: [u8; 256]) -> Self {
|
||||||
|
Self::from_payload(PingRequestPayload { echo })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for PingRequest {
|
||||||
|
type Payload = PingRequestPayload;
|
||||||
|
type MessageClass = RequestMsgType;
|
||||||
|
const MESSAGE_TYPE: Self::MessageClass = RequestMsgType::Ping;
|
||||||
|
|
||||||
|
fn from_payload(payload: Self::Payload) -> Self {
|
||||||
|
Self {
|
||||||
|
msg_type: Self::MESSAGE_TYPE.into(),
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>> {
|
||||||
|
let mut r: Ref<B, Self> = buf.zk_zeroized()?;
|
||||||
|
r.init();
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) {
|
||||||
|
self.msg_type = Self::MESSAGE_TYPE.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
|
||||||
|
pub struct PingResponsePayload {
|
||||||
|
/// Randomly generated connection id
|
||||||
|
pub echo: [u8; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PingResponse = ResponseEnvelope<PingResponsePayload>;
|
||||||
|
|
||||||
|
impl PingResponse {
|
||||||
|
pub fn new(echo: [u8; 256]) -> Self {
|
||||||
|
Self::from_payload(PingResponsePayload { echo })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message for PingResponse {
|
||||||
|
type Payload = PingResponsePayload;
|
||||||
|
type MessageClass = ResponseMsgType;
|
||||||
|
const MESSAGE_TYPE: Self::MessageClass = ResponseMsgType::Ping;
|
||||||
|
|
||||||
|
fn from_payload(payload: Self::Payload) -> Self {
|
||||||
|
Self {
|
||||||
|
msg_type: Self::MESSAGE_TYPE.into(),
|
||||||
|
payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>> {
|
||||||
|
let mut r: Ref<B, Self> = buf.zk_zeroized()?;
|
||||||
|
r.init();
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) {
|
||||||
|
self.msg_type = Self::MESSAGE_TYPE.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
107
rosenpass/src/api/boilerplate/request_ref.rs
Normal file
107
rosenpass/src/api/boilerplate/request_ref.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
use anyhow::ensure;
|
||||||
|
|
||||||
|
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||||
|
|
||||||
|
use super::{ByteSliceRefExt, MessageAttributes, PingRequest, RequestMsgType};
|
||||||
|
|
||||||
|
struct RequestRefMaker<B> {
|
||||||
|
buf: B,
|
||||||
|
msg_type: RequestMsgType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSlice> RequestRef<B> {
|
||||||
|
pub fn parse(buf: B) -> anyhow::Result<Self> {
|
||||||
|
RequestRefMaker::new(buf)?.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_from_prefix(buf: B) -> anyhow::Result<Self> {
|
||||||
|
RequestRefMaker::new(buf)?.from_prefix()?.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_from_suffix(buf: B) -> anyhow::Result<Self> {
|
||||||
|
RequestRefMaker::new(buf)?.from_suffix()?.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_type(&self) -> RequestMsgType {
|
||||||
|
match self {
|
||||||
|
Self::Ping(_) => RequestMsgType::Ping,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> From<Ref<B, PingRequest>> for RequestRef<B> {
|
||||||
|
fn from(v: Ref<B, PingRequest>) -> Self {
|
||||||
|
Self::Ping(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSlice> RequestRefMaker<B> {
|
||||||
|
fn new(buf: B) -> anyhow::Result<Self> {
|
||||||
|
let msg_type = buf.deref().request_msg_type_from_prefix()?;
|
||||||
|
Ok(Self { buf, msg_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_size(&self) -> usize {
|
||||||
|
self.msg_type.message_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(self) -> anyhow::Result<RequestRef<B>> {
|
||||||
|
Ok(match self.msg_type {
|
||||||
|
RequestMsgType::Ping => RequestRef::Ping(self.buf.ping_request()?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
fn from_prefix(self) -> anyhow::Result<Self> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
let point = self.target_size();
|
||||||
|
let Self { buf, msg_type } = self;
|
||||||
|
let (buf, _) = buf.split_at(point);
|
||||||
|
Ok(Self { buf, msg_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
fn from_suffix(self) -> anyhow::Result<Self> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
let point = self.buf.len() - self.target_size();
|
||||||
|
let Self { buf, msg_type } = self;
|
||||||
|
let (buf, _) = buf.split_at(point);
|
||||||
|
Ok(Self { buf, msg_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_fit(&self) -> anyhow::Result<()> {
|
||||||
|
let have = self.buf.len();
|
||||||
|
let need = self.target_size();
|
||||||
|
ensure!(
|
||||||
|
need <= have,
|
||||||
|
"Buffer is undersized at {have} bytes (need {need} bytes)!"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum RequestRef<B> {
|
||||||
|
Ping(Ref<B, PingRequest>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> RequestRef<B>
|
||||||
|
where
|
||||||
|
B: ByteSlice,
|
||||||
|
{
|
||||||
|
pub fn bytes(&self) -> &[u8] {
|
||||||
|
match self {
|
||||||
|
Self::Ping(r) => r.bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> RequestRef<B>
|
||||||
|
where
|
||||||
|
B: ByteSliceMut,
|
||||||
|
{
|
||||||
|
pub fn bytes_mut(&mut self) -> &[u8] {
|
||||||
|
match self {
|
||||||
|
Self::Ping(r) => r.bytes_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
103
rosenpass/src/api/boilerplate/request_response.rs
Normal file
103
rosenpass/src/api/boilerplate/request_response.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
use rosenpass_util::zerocopy::{
|
||||||
|
RefMaker, ZerocopyEmancipateExt, ZerocopyEmancipateMutExt, ZerocopySliceExt,
|
||||||
|
};
|
||||||
|
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||||
|
|
||||||
|
use super::{Message, PingRequest, PingResponse};
|
||||||
|
use super::{RequestRef, ResponseRef, ZerocopyResponseMakerSetupMessageExt};
|
||||||
|
|
||||||
|
pub trait RequestMsg: Sized + Message {
|
||||||
|
type ResponseMsg: ResponseMsg;
|
||||||
|
|
||||||
|
fn zk_response_maker<B: ByteSlice>(buf: B) -> RefMaker<B, Self::ResponseMsg> {
|
||||||
|
buf.zk_ref_maker()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_response<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
|
||||||
|
Self::zk_response_maker(buf).setup_msg()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_response_from_prefix<B: ByteSliceMut>(
|
||||||
|
buf: B,
|
||||||
|
) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
|
||||||
|
Self::zk_response_maker(buf).from_prefix()?.setup_msg()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_response_from_suffix<B: ByteSliceMut>(
|
||||||
|
buf: B,
|
||||||
|
) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
|
||||||
|
Self::zk_response_maker(buf).from_prefix()?.setup_msg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ResponseMsg: Message {
|
||||||
|
type RequestMsg: RequestMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestMsg for PingRequest {
|
||||||
|
type ResponseMsg = PingResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResponseMsg for PingResponse {
|
||||||
|
type RequestMsg = PingRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PingPair<B1, B2> = (Ref<B1, PingRequest>, Ref<B2, PingResponse>);
|
||||||
|
|
||||||
|
pub enum RequestResponsePair<B1, B2> {
|
||||||
|
Ping(PingPair<B1, B2>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B1, B2> From<PingPair<B1, B2>> for RequestResponsePair<B1, B2> {
|
||||||
|
fn from(v: PingPair<B1, B2>) -> Self {
|
||||||
|
RequestResponsePair::Ping(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B1, B2> RequestResponsePair<B1, B2>
|
||||||
|
where
|
||||||
|
B1: ByteSlice,
|
||||||
|
B2: ByteSlice,
|
||||||
|
{
|
||||||
|
pub fn both(&self) -> (RequestRef<&[u8]>, ResponseRef<&[u8]>) {
|
||||||
|
match self {
|
||||||
|
Self::Ping((req, res)) => {
|
||||||
|
let req = RequestRef::Ping(req.emancipate());
|
||||||
|
let res = ResponseRef::Ping(res.emancipate());
|
||||||
|
(req, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request(&self) -> RequestRef<&[u8]> {
|
||||||
|
self.both().0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn response(&self) -> ResponseRef<&[u8]> {
|
||||||
|
self.both().1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B1, B2> RequestResponsePair<B1, B2>
|
||||||
|
where
|
||||||
|
B1: ByteSliceMut,
|
||||||
|
B2: ByteSliceMut,
|
||||||
|
{
|
||||||
|
pub fn both_mut(&mut self) -> (RequestRef<&mut [u8]>, ResponseRef<&mut [u8]>) {
|
||||||
|
match self {
|
||||||
|
Self::Ping((req, res)) => {
|
||||||
|
let req = RequestRef::Ping(req.emancipate_mut());
|
||||||
|
let res = ResponseRef::Ping(res.emancipate_mut());
|
||||||
|
(req, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_mut(&mut self) -> RequestRef<&mut [u8]> {
|
||||||
|
self.both_mut().0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn response_mut(&mut self) -> ResponseRef<&mut [u8]> {
|
||||||
|
self.both_mut().1
|
||||||
|
}
|
||||||
|
}
|
||||||
108
rosenpass/src/api/boilerplate/response_ref.rs
Normal file
108
rosenpass/src/api/boilerplate/response_ref.rs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
// TODO: This is copied verbatim from ResponseRef…not pretty
|
||||||
|
use anyhow::ensure;
|
||||||
|
|
||||||
|
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||||
|
|
||||||
|
use super::{ByteSliceRefExt, MessageAttributes, PingResponse, ResponseMsgType};
|
||||||
|
|
||||||
|
struct ResponseRefMaker<B> {
|
||||||
|
buf: B,
|
||||||
|
msg_type: ResponseMsgType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSlice> ResponseRef<B> {
|
||||||
|
pub fn parse(buf: B) -> anyhow::Result<Self> {
|
||||||
|
ResponseRefMaker::new(buf)?.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_from_prefix(buf: B) -> anyhow::Result<Self> {
|
||||||
|
ResponseRefMaker::new(buf)?.from_prefix()?.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_from_suffix(buf: B) -> anyhow::Result<Self> {
|
||||||
|
ResponseRefMaker::new(buf)?.from_suffix()?.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_type(&self) -> ResponseMsgType {
|
||||||
|
match self {
|
||||||
|
Self::Ping(_) => ResponseMsgType::Ping,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> From<Ref<B, PingResponse>> for ResponseRef<B> {
|
||||||
|
fn from(v: Ref<B, PingResponse>) -> Self {
|
||||||
|
Self::Ping(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSlice> ResponseRefMaker<B> {
|
||||||
|
fn new(buf: B) -> anyhow::Result<Self> {
|
||||||
|
let msg_type = buf.deref().response_msg_type_from_prefix()?;
|
||||||
|
Ok(Self { buf, msg_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_size(&self) -> usize {
|
||||||
|
self.msg_type.message_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(self) -> anyhow::Result<ResponseRef<B>> {
|
||||||
|
Ok(match self.msg_type {
|
||||||
|
ResponseMsgType::Ping => ResponseRef::Ping(self.buf.ping_response()?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
fn from_prefix(self) -> anyhow::Result<Self> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
let point = self.target_size();
|
||||||
|
let Self { buf, msg_type } = self;
|
||||||
|
let (buf, _) = buf.split_at(point);
|
||||||
|
Ok(Self { buf, msg_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
fn from_suffix(self) -> anyhow::Result<Self> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
let point = self.buf.len() - self.target_size();
|
||||||
|
let Self { buf, msg_type } = self;
|
||||||
|
let (buf, _) = buf.split_at(point);
|
||||||
|
Ok(Self { buf, msg_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_fit(&self) -> anyhow::Result<()> {
|
||||||
|
let have = self.buf.len();
|
||||||
|
let need = self.target_size();
|
||||||
|
ensure!(
|
||||||
|
need <= have,
|
||||||
|
"Buffer is undersized at {have} bytes (need {need} bytes)!"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ResponseRef<B> {
|
||||||
|
Ping(Ref<B, PingResponse>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> ResponseRef<B>
|
||||||
|
where
|
||||||
|
B: ByteSlice,
|
||||||
|
{
|
||||||
|
pub fn bytes(&self) -> &[u8] {
|
||||||
|
match self {
|
||||||
|
Self::Ping(r) => r.bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> ResponseRef<B>
|
||||||
|
where
|
||||||
|
B: ByteSliceMut,
|
||||||
|
{
|
||||||
|
pub fn bytes_mut(&mut self) -> &[u8] {
|
||||||
|
match self {
|
||||||
|
Self::Ping(r) => r.bytes_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
rosenpass/src/api/boilerplate/server.rs
Normal file
40
rosenpass/src/api/boilerplate/server.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
use zerocopy::{ByteSlice, ByteSliceMut};
|
||||||
|
|
||||||
|
use super::{ByteSliceRefExt, Message, PingRequest, PingResponse, RequestRef, RequestResponsePair};
|
||||||
|
|
||||||
|
pub trait Server {
|
||||||
|
fn ping(&mut self, req: &PingRequest, res: &mut PingResponse) -> anyhow::Result<()>;
|
||||||
|
|
||||||
|
fn dispatch<ReqBuf, ResBuf>(
|
||||||
|
&mut self,
|
||||||
|
p: &mut RequestResponsePair<ReqBuf, ResBuf>,
|
||||||
|
) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
ReqBuf: ByteSlice,
|
||||||
|
ResBuf: ByteSliceMut,
|
||||||
|
{
|
||||||
|
match p {
|
||||||
|
RequestResponsePair::Ping((req, res)) => self.ping(req, res),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_message<ReqBuf, ResBuf>(&mut self, req: ReqBuf, res: ResBuf) -> anyhow::Result<usize>
|
||||||
|
where
|
||||||
|
ReqBuf: ByteSlice,
|
||||||
|
ResBuf: ByteSliceMut,
|
||||||
|
{
|
||||||
|
let req = req.parse_request_from_prefix()?;
|
||||||
|
// TODO: This is not pretty; This match should be moved into RequestRef
|
||||||
|
let mut pair = match req {
|
||||||
|
RequestRef::Ping(req) => {
|
||||||
|
let mut res = res.ping_response_from_prefix()?;
|
||||||
|
res.init();
|
||||||
|
RequestResponsePair::Ping((req, res))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.dispatch(&mut pair)?;
|
||||||
|
|
||||||
|
let res_len = pair.request().bytes().len();
|
||||||
|
Ok(res_len)
|
||||||
|
}
|
||||||
|
}
|
||||||
40
rosenpass/src/api/cli.rs
Normal file
40
rosenpass/src/api/cli.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::Args;
|
||||||
|
|
||||||
|
use crate::config::Rosenpass as RosenpassConfig;
|
||||||
|
|
||||||
|
use super::config::ApiConfig;
|
||||||
|
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
pub struct ApiCli {
|
||||||
|
/// Where in the file-system to create the unix socket the rosenpass API will be listening for
|
||||||
|
/// connections on.
|
||||||
|
#[arg(long)]
|
||||||
|
api_listen_path: Vec<PathBuf>,
|
||||||
|
|
||||||
|
/// When rosenpass is called from another process, the other process can open and bind the
|
||||||
|
/// unix socket for the Rosenpass API to use themselves, passing it to this process. In Rust this can be achieved
|
||||||
|
/// using the [command-fds](https://docs.rs/command-fds/latest/command_fds/) crate.
|
||||||
|
#[arg(long)]
|
||||||
|
api_listen_fd: Vec<i32>,
|
||||||
|
|
||||||
|
/// When rosenpass is called from another process, the other process can connect the unix socket for the API
|
||||||
|
/// themselves, for instance using the `socketpair(2)` system call.
|
||||||
|
#[arg(long)]
|
||||||
|
api_stream_fd: Vec<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiCli {
|
||||||
|
pub fn apply_to_config(&self, cfg: &mut RosenpassConfig) -> anyhow::Result<()> {
|
||||||
|
self.apply_to_api_config(&mut cfg.api)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_to_api_config(&self, cfg: &mut ApiConfig) -> anyhow::Result<()> {
|
||||||
|
cfg.listen_path.extend_from_slice(&self.api_listen_path);
|
||||||
|
cfg.listen_fd.extend_from_slice(&self.api_listen_fd);
|
||||||
|
cfg.stream_fd.extend_from_slice(&self.api_stream_fd);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
41
rosenpass/src/api/config.rs
Normal file
41
rosenpass/src/api/config.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use mio::net::UnixListener;
|
||||||
|
use rosenpass_util::mio::{UnixListenerExt, UnixStreamExt};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::app_server::AppServer;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||||
|
pub struct ApiConfig {
|
||||||
|
/// Where in the file-system to create the unix socket the rosenpass API will be listening for
|
||||||
|
/// connections on
|
||||||
|
pub listen_path: Vec<PathBuf>,
|
||||||
|
|
||||||
|
/// When rosenpass is called from another process, the other process can open and bind the
|
||||||
|
/// unix socket for the Rosenpass API to use themselves, passing it to this process. In Rust this can be achieved
|
||||||
|
/// using the [command-fds](https://docs.rs/command-fds/latest/command_fds/) crate.
|
||||||
|
pub listen_fd: Vec<i32>,
|
||||||
|
|
||||||
|
/// When rosenpass is called from another process, the other process can connect the unix socket for the API
|
||||||
|
/// themselves, for instance using the `socketpair(2)` system call.
|
||||||
|
pub stream_fd: Vec<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiConfig {
|
||||||
|
pub fn apply_to_app_server(&self, srv: &mut AppServer) -> anyhow::Result<()> {
|
||||||
|
for path in self.listen_path.iter() {
|
||||||
|
srv.add_api_listener(UnixListener::bind(path)?)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for fd in self.listen_fd.iter() {
|
||||||
|
srv.add_api_listener(UnixListenerExt::claim_fd(*fd)?)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for fd in self.stream_fd.iter() {
|
||||||
|
srv.add_api_connection(UnixStreamExt::claim_fd(*fd)?)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
44
rosenpass/src/api/crypto_server_api_handler.rs
Normal file
44
rosenpass/src/api/crypto_server_api_handler.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use rosenpass_to::{ops::copy_slice, To};
|
||||||
|
|
||||||
|
use crate::protocol::CryptoServer;
|
||||||
|
|
||||||
|
use super::Server as ApiServer;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CryptoServerApiState {
|
||||||
|
_dummy: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CryptoServerApiState {
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { _dummy: () }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire_backend<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
crypto: &'a mut Option<CryptoServer>,
|
||||||
|
) -> CryptoServerApiHandler<'a> {
|
||||||
|
let state = self;
|
||||||
|
CryptoServerApiHandler { state, crypto }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CryptoServerApiHandler<'a> {
|
||||||
|
#[allow(unused)] // TODO: Remove
|
||||||
|
crypto: &'a mut Option<CryptoServer>,
|
||||||
|
#[allow(unused)] // TODO: Remove
|
||||||
|
state: &'a mut CryptoServerApiState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ApiServer for CryptoServerApiHandler<'a> {
|
||||||
|
fn ping(
|
||||||
|
&mut self,
|
||||||
|
req: &super::PingRequest,
|
||||||
|
res: &mut super::PingResponse,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let (req, res) = (&req.payload, &mut res.payload);
|
||||||
|
copy_slice(&req.echo).to(&mut res.echo);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
167
rosenpass/src/api/mio/connection.rs
Normal file
167
rosenpass/src/api/mio/connection.rs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
use mio::{net::UnixStream, Interest};
|
||||||
|
use rosenpass_util::{
|
||||||
|
io::{IoResultKindHintExt, TryIoResultKindHintExt},
|
||||||
|
length_prefix_encoding::{
|
||||||
|
decoder::{self as lpe_decoder, LengthPrefixDecoder},
|
||||||
|
encoder::{self as lpe_encoder, LengthPrefixEncoder},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use crate::{api::Server, app_server::MioTokenDispenser, protocol::CryptoServer};
|
||||||
|
|
||||||
|
use super::super::{CryptoServerApiState, MAX_REQUEST_LEN, MAX_RESPONSE_LEN};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MioConnection {
|
||||||
|
io: UnixStream,
|
||||||
|
invalid_read: bool,
|
||||||
|
read_buffer: LengthPrefixDecoder<[u8; MAX_REQUEST_LEN]>,
|
||||||
|
write_buffer: LengthPrefixEncoder<[u8; MAX_RESPONSE_LEN]>,
|
||||||
|
api_state: CryptoServerApiState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MioConnection {
|
||||||
|
pub fn new(
|
||||||
|
mut io: UnixStream,
|
||||||
|
registry: &mio::Registry,
|
||||||
|
token_dispenser: &mut MioTokenDispenser, // TODO: We should actually start using tokens…
|
||||||
|
) -> std::io::Result<Self> {
|
||||||
|
registry.register(
|
||||||
|
&mut io,
|
||||||
|
token_dispenser.dispense(),
|
||||||
|
Interest::READABLE | Interest::WRITABLE,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let invalid_read = false;
|
||||||
|
let read_buffer = LengthPrefixDecoder::new([0u8; MAX_REQUEST_LEN]);
|
||||||
|
let write_buffer = LengthPrefixEncoder::from_buffer([0u8; MAX_RESPONSE_LEN]);
|
||||||
|
let api_state = CryptoServerApiState::new();
|
||||||
|
Ok(Self {
|
||||||
|
io,
|
||||||
|
invalid_read,
|
||||||
|
read_buffer,
|
||||||
|
write_buffer,
|
||||||
|
api_state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll(&mut self, crypto: &mut Option<CryptoServer>) -> anyhow::Result<()> {
|
||||||
|
self.flush_write_buffer()?;
|
||||||
|
if self.write_buffer.exhausted() {
|
||||||
|
self.recv(crypto)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is *exclusively* called by recv if the read_buffer holds a message
|
||||||
|
fn handle_incoming_message(&mut self, crypto: &mut Option<CryptoServer>) -> anyhow::Result<()> {
|
||||||
|
// Unwrap is allowed because recv() confirms before the call that a message was
|
||||||
|
// received
|
||||||
|
let req = self.read_buffer.message().unwrap().unwrap();
|
||||||
|
|
||||||
|
// TODO: The API should not return anyhow::Result
|
||||||
|
let response_len = self
|
||||||
|
.api_state
|
||||||
|
.acquire_backend(crypto)
|
||||||
|
.handle_message(req, self.write_buffer.buffer_bytes_mut())?;
|
||||||
|
self.read_buffer.zeroize(); // clear for new message to read
|
||||||
|
self.write_buffer
|
||||||
|
.restart_write_with_new_message(response_len)?;
|
||||||
|
|
||||||
|
self.flush_write_buffer()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush_write_buffer(&mut self) -> anyhow::Result<()> {
|
||||||
|
if self.write_buffer.exhausted() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
use lpe_encoder::WriteToIoReturn as Ret;
|
||||||
|
use std::io::ErrorKind as K;
|
||||||
|
|
||||||
|
match self
|
||||||
|
.write_buffer
|
||||||
|
.write_to_stdio(&self.io)
|
||||||
|
.io_err_kind_hint()
|
||||||
|
{
|
||||||
|
// Done
|
||||||
|
Ok(Ret { done: true, .. }) => {
|
||||||
|
self.write_buffer.zeroize(); // clear for new message to write
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Would block
|
||||||
|
Ok(Ret {
|
||||||
|
bytes_written: 0, ..
|
||||||
|
}) => break,
|
||||||
|
Err((_e, K::WouldBlock)) => break,
|
||||||
|
|
||||||
|
// Just continue
|
||||||
|
Ok(_) => continue, /* Ret { bytes_written > 0, done = false } acc. to previous cases*/
|
||||||
|
Err((_e, K::Interrupted)) => continue,
|
||||||
|
|
||||||
|
// Other errors
|
||||||
|
Err((e, _ek)) => Err(e)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recv(&mut self, crypto: &mut Option<CryptoServer>) -> anyhow::Result<()> {
|
||||||
|
if !self.write_buffer.exhausted() || self.invalid_read {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
use lpe_decoder::{ReadFromIoError as E, ReadFromIoReturn as Ret};
|
||||||
|
use std::io::ErrorKind as K;
|
||||||
|
|
||||||
|
match self
|
||||||
|
.read_buffer
|
||||||
|
.read_from_stdio(&self.io)
|
||||||
|
.try_io_err_kind_hint()
|
||||||
|
{
|
||||||
|
// We actually received a proper message
|
||||||
|
// (Impl below match to appease borrow checker)
|
||||||
|
Ok(Ret {
|
||||||
|
message: Some(_msg),
|
||||||
|
..
|
||||||
|
}) => {}
|
||||||
|
|
||||||
|
// Message does not fit in buffer
|
||||||
|
Err((e @ E::MessageTooLargeError { .. }, _)) => {
|
||||||
|
log::warn!("Received message on API that was too big to fit in our buffers; \
|
||||||
|
looks like the client is broken. Stopping to process messages of the client.\n\
|
||||||
|
Error: {e:?}");
|
||||||
|
// TODO: We should properly close down the socket in this case, but to do that,
|
||||||
|
// we need to have the facilities in the Rosenpass IO handling system to close
|
||||||
|
// open connections.
|
||||||
|
// Just leaving the API connections dangling for now.
|
||||||
|
// This should be fixed for non-experimental use of the API.
|
||||||
|
self.invalid_read = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Would block
|
||||||
|
Ok(Ret { bytes_read: 0, .. }) => break,
|
||||||
|
Err((_, Some(K::WouldBlock))) => break,
|
||||||
|
|
||||||
|
// Just keep going
|
||||||
|
Ok(Ret { bytes_read: _, .. }) => continue,
|
||||||
|
Err((_, Some(K::Interrupted))) => continue,
|
||||||
|
|
||||||
|
// Other IO Error (just pass on to the caller)
|
||||||
|
Err((E::IoError(e), _)) => Err(e)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.handle_incoming_message(crypto)?;
|
||||||
|
break; // Handle just one message, leave some room for other IO handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
93
rosenpass/src/api/mio/manager.rs
Normal file
93
rosenpass/src/api/mio/manager.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
use std::io;
|
||||||
|
|
||||||
|
use mio::net::{UnixListener, UnixStream};
|
||||||
|
|
||||||
|
use rosenpass_util::{io::nonblocking_handle_io_errors, mio::interest::RW as MIO_RW};
|
||||||
|
|
||||||
|
use crate::{app_server::MioTokenDispenser, protocol::CryptoServer};
|
||||||
|
|
||||||
|
use super::MioConnection;
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct MioManager {
|
||||||
|
listeners: Vec<UnixListener>,
|
||||||
|
connections: Vec<MioConnection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MioManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_listener(
|
||||||
|
&mut self,
|
||||||
|
mut listener: UnixListener,
|
||||||
|
registry: &mio::Registry,
|
||||||
|
token_dispenser: &mut MioTokenDispenser,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
registry.register(&mut listener, token_dispenser.dispense(), MIO_RW)?;
|
||||||
|
self.listeners.push(listener);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_connection(
|
||||||
|
&mut self,
|
||||||
|
connection: UnixStream,
|
||||||
|
registry: &mio::Registry,
|
||||||
|
token_dispenser: &mut MioTokenDispenser,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let connection = MioConnection::new(connection, registry, token_dispenser)?;
|
||||||
|
self.connections.push(connection);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll(
|
||||||
|
&mut self,
|
||||||
|
crypto: &mut Option<CryptoServer>,
|
||||||
|
registry: &mio::Registry,
|
||||||
|
token_dispenser: &mut MioTokenDispenser,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
self.accept_connections(registry, token_dispenser)?;
|
||||||
|
self.poll_connections(crypto)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accept_connections(
|
||||||
|
&mut self,
|
||||||
|
registry: &mio::Registry,
|
||||||
|
token_dispenser: &mut MioTokenDispenser,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
for idx in 0..self.listeners.len() {
|
||||||
|
self.accept_from(idx, registry, token_dispenser)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accept_from(
|
||||||
|
&mut self,
|
||||||
|
idx: usize,
|
||||||
|
registry: &mio::Registry,
|
||||||
|
token_dispenser: &mut MioTokenDispenser,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
// Accept connection until the socket would block or returns another error
|
||||||
|
// TODO: This currently only adds connections--we eventually need the ability to remove
|
||||||
|
// them as well, see the note in connection.rs
|
||||||
|
loop {
|
||||||
|
match nonblocking_handle_io_errors(|| self.listeners[idx].accept())? {
|
||||||
|
None => break,
|
||||||
|
Some((conn, _addr)) => {
|
||||||
|
self.add_connection(conn, registry, token_dispenser)?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_connections(&mut self, crypto: &mut Option<CryptoServer>) -> anyhow::Result<()> {
|
||||||
|
for conn in self.connections.iter_mut() {
|
||||||
|
conn.poll(crypto)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
5
rosenpass/src/api/mio/mod.rs
Normal file
5
rosenpass/src/api/mio/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
mod connection;
|
||||||
|
mod manager;
|
||||||
|
|
||||||
|
pub use connection::*;
|
||||||
|
pub use manager::*;
|
||||||
9
rosenpass/src/api/mod.rs
Normal file
9
rosenpass/src/api/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
mod boilerplate;
|
||||||
|
mod crypto_server_api_handler;
|
||||||
|
|
||||||
|
pub use boilerplate::*;
|
||||||
|
pub use crypto_server_api_handler::*;
|
||||||
|
|
||||||
|
pub mod cli;
|
||||||
|
pub mod config;
|
||||||
|
pub mod mio;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
@@ -16,7 +17,9 @@ use std::cell::Cell;
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use std::io::stdout;
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
|
use std::io::Write;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::net::Ipv6Addr;
|
use std::net::Ipv6Addr;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
@@ -63,7 +66,7 @@ pub struct MioTokenDispenser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MioTokenDispenser {
|
impl MioTokenDispenser {
|
||||||
fn dispense(&mut self) -> Token {
|
pub fn dispense(&mut self) -> Token {
|
||||||
let r = self.counter;
|
let r = self.counter;
|
||||||
self.counter += 1;
|
self.counter += 1;
|
||||||
Token(r)
|
Token(r)
|
||||||
@@ -144,7 +147,7 @@ pub struct AppServerTest {
|
|||||||
// TODO add user control via unix domain socket and stdin/stdout
|
// TODO add user control via unix domain socket and stdin/stdout
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AppServer {
|
pub struct AppServer {
|
||||||
pub crypt: CryptoServer,
|
pub crypt: Option<CryptoServer>,
|
||||||
pub sockets: Vec<mio::net::UdpSocket>,
|
pub sockets: Vec<mio::net::UdpSocket>,
|
||||||
pub events: mio::Events,
|
pub events: mio::Events,
|
||||||
pub mio_poll: mio::Poll,
|
pub mio_poll: mio::Poll,
|
||||||
@@ -159,6 +162,8 @@ pub struct AppServer {
|
|||||||
pub unpolled_count: usize,
|
pub unpolled_count: usize,
|
||||||
pub last_update_time: Instant,
|
pub last_update_time: Instant,
|
||||||
pub test_helpers: Option<AppServerTest>,
|
pub test_helpers: Option<AppServerTest>,
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
pub api_manager: crate::api::mio::MioManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A socket pointer is an index assigned to a socket;
|
/// A socket pointer is an index assigned to a socket;
|
||||||
@@ -601,7 +606,7 @@ impl AppServer {
|
|||||||
// TODO use mio::net::UnixStream together with std::os::unix::net::UnixStream for Linux
|
// TODO use mio::net::UnixStream together with std::os::unix::net::UnixStream for Linux
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
crypt: CryptoServer::new(sk, pk),
|
crypt: Some(CryptoServer::new(sk, pk)),
|
||||||
peers: Vec::new(),
|
peers: Vec::new(),
|
||||||
verbosity,
|
verbosity,
|
||||||
sockets,
|
sockets,
|
||||||
@@ -616,9 +621,23 @@ impl AppServer {
|
|||||||
unpolled_count: 0,
|
unpolled_count: 0,
|
||||||
last_update_time: Instant::now(),
|
last_update_time: Instant::now(),
|
||||||
test_helpers,
|
test_helpers,
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
api_manager: crate::api::mio::MioManager::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn crypto_server(&self) -> anyhow::Result<&CryptoServer> {
|
||||||
|
self.crypt
|
||||||
|
.as_ref()
|
||||||
|
.context("Cryptography handler not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn crypto_server_mut(&mut self) -> anyhow::Result<&mut CryptoServer> {
|
||||||
|
self.crypt
|
||||||
|
.as_mut()
|
||||||
|
.context("Cryptography handler not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn verbose(&self) -> bool {
|
pub fn verbose(&self) -> bool {
|
||||||
matches!(self.verbosity, Verbosity::Verbose)
|
matches!(self.verbosity, Verbosity::Verbose)
|
||||||
}
|
}
|
||||||
@@ -669,7 +688,7 @@ impl AppServer {
|
|||||||
broker_peer: Option<BrokerPeer>,
|
broker_peer: Option<BrokerPeer>,
|
||||||
hostname: Option<String>,
|
hostname: Option<String>,
|
||||||
) -> anyhow::Result<AppPeerPtr> {
|
) -> anyhow::Result<AppPeerPtr> {
|
||||||
let PeerPtr(pn) = self.crypt.add_peer(psk, pk)?;
|
let PeerPtr(pn) = self.crypto_server_mut()?.add_peer(psk, pk)?;
|
||||||
assert!(pn == self.peers.len());
|
assert!(pn == self.peers.len());
|
||||||
let initial_endpoint = hostname
|
let initial_endpoint = hostname
|
||||||
.map(Endpoint::discovery_from_hostname)
|
.map(Endpoint::discovery_from_hostname)
|
||||||
@@ -712,7 +731,7 @@ impl AppServer {
|
|||||||
);
|
);
|
||||||
if tries_left > 0 {
|
if tries_left > 0 {
|
||||||
error!("re-initializing networking in {sleep}! {tries_left} tries left.");
|
error!("re-initializing networking in {sleep}! {tries_left} tries left.");
|
||||||
std::thread::sleep(self.crypt.timebase.dur(sleep));
|
std::thread::sleep(self.crypto_server_mut()?.timebase.dur(sleep));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -758,11 +777,11 @@ impl AppServer {
|
|||||||
match self.poll(&mut *rx)? {
|
match self.poll(&mut *rx)? {
|
||||||
#[allow(clippy::redundant_closure_call)]
|
#[allow(clippy::redundant_closure_call)]
|
||||||
SendInitiation(peer) => tx_maybe_with!(peer, || self
|
SendInitiation(peer) => tx_maybe_with!(peer, || self
|
||||||
.crypt
|
.crypto_server_mut()?
|
||||||
.initiate_handshake(peer.lower(), &mut *tx))?,
|
.initiate_handshake(peer.lower(), &mut *tx))?,
|
||||||
#[allow(clippy::redundant_closure_call)]
|
#[allow(clippy::redundant_closure_call)]
|
||||||
SendRetransmission(peer) => tx_maybe_with!(peer, || self
|
SendRetransmission(peer) => tx_maybe_with!(peer, || self
|
||||||
.crypt
|
.crypto_server_mut()?
|
||||||
.retransmit_handshake(peer.lower(), &mut *tx))?,
|
.retransmit_handshake(peer.lower(), &mut *tx))?,
|
||||||
DeleteKey(peer) => {
|
DeleteKey(peer) => {
|
||||||
self.output_key(peer, Stale, &SymKey::random())?;
|
self.output_key(peer, Stale, &SymKey::random())?;
|
||||||
@@ -783,7 +802,9 @@ impl AppServer {
|
|||||||
DoSOperation::UnderLoad => {
|
DoSOperation::UnderLoad => {
|
||||||
self.handle_msg_under_load(&endpoint, &rx[..len], &mut *tx)
|
self.handle_msg_under_load(&endpoint, &rx[..len], &mut *tx)
|
||||||
}
|
}
|
||||||
DoSOperation::Normal => self.crypt.handle_msg(&rx[..len], &mut *tx),
|
DoSOperation::Normal => {
|
||||||
|
self.crypto_server_mut()?.handle_msg(&rx[..len], &mut *tx)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match msg_result {
|
match msg_result {
|
||||||
Err(ref e) => {
|
Err(ref e) => {
|
||||||
@@ -811,7 +832,8 @@ impl AppServer {
|
|||||||
ap.get_app_mut(self).current_endpoint = Some(endpoint);
|
ap.get_app_mut(self).current_endpoint = Some(endpoint);
|
||||||
|
|
||||||
// TODO: Maybe we should rather call the key "rosenpass output"?
|
// TODO: Maybe we should rather call the key "rosenpass output"?
|
||||||
self.output_key(ap, Exchanged, &self.crypt.osk(p)?)?;
|
let osk = &self.crypto_server_mut()?.osk(p)?;
|
||||||
|
self.output_key(ap, Exchanged, osk)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -827,9 +849,9 @@ impl AppServer {
|
|||||||
tx: &mut [u8],
|
tx: &mut [u8],
|
||||||
) -> Result<crate::protocol::HandleMsgResult> {
|
) -> Result<crate::protocol::HandleMsgResult> {
|
||||||
match endpoint {
|
match endpoint {
|
||||||
Endpoint::SocketBoundAddress(socket) => {
|
Endpoint::SocketBoundAddress(socket) => self
|
||||||
self.crypt.handle_msg_under_load(rx, &mut *tx, socket)
|
.crypto_server_mut()?
|
||||||
}
|
.handle_msg_under_load(rx, &mut *tx, socket),
|
||||||
Endpoint::Discovery(_) => {
|
Endpoint::Discovery(_) => {
|
||||||
anyhow::bail!("Host-path discovery is not supported under load")
|
anyhow::bail!("Host-path discovery is not supported under load")
|
||||||
}
|
}
|
||||||
@@ -842,7 +864,7 @@ impl AppServer {
|
|||||||
why: KeyOutputReason,
|
why: KeyOutputReason,
|
||||||
key: &SymKey,
|
key: &SymKey,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let peerid = peer.lower().get(&self.crypt).pidt()?;
|
let peerid = peer.lower().get(self.crypto_server()?).pidt()?;
|
||||||
|
|
||||||
if self.verbose() {
|
if self.verbose() {
|
||||||
let msg = match why {
|
let msg = match why {
|
||||||
@@ -870,10 +892,14 @@ impl AppServer {
|
|||||||
|
|
||||||
// this is intentionally writing to stdout instead of stderr, because
|
// this is intentionally writing to stdout instead of stderr, because
|
||||||
// it is meant to allow external detection of a successful key-exchange
|
// it is meant to allow external detection of a successful key-exchange
|
||||||
println!(
|
let stdout = stdout();
|
||||||
|
let mut stdout = stdout.lock();
|
||||||
|
writeln!(
|
||||||
|
stdout,
|
||||||
"output-key peer {} key-file {of:?} {why}",
|
"output-key peer {} key-file {of:?} {why}",
|
||||||
peerid.fmt_b64::<MAX_B64_PEER_ID_SIZE>()
|
peerid.fmt_b64::<MAX_B64_PEER_ID_SIZE>()
|
||||||
);
|
)?;
|
||||||
|
stdout.flush()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.set_psk(self, key)?;
|
peer.set_psk(self, key)?;
|
||||||
@@ -885,7 +911,7 @@ impl AppServer {
|
|||||||
use crate::protocol::PollResult as C;
|
use crate::protocol::PollResult as C;
|
||||||
use AppPollResult as A;
|
use AppPollResult as A;
|
||||||
loop {
|
loop {
|
||||||
return Ok(match self.crypt.poll()? {
|
return Ok(match self.crypto_server_mut()?.poll()? {
|
||||||
C::DeleteKey(PeerPtr(no)) => A::DeleteKey(AppPeerPtr(no)),
|
C::DeleteKey(PeerPtr(no)) => A::DeleteKey(AppPeerPtr(no)),
|
||||||
C::SendInitiation(PeerPtr(no)) => A::SendInitiation(AppPeerPtr(no)),
|
C::SendInitiation(PeerPtr(no)) => A::SendInitiation(AppPeerPtr(no)),
|
||||||
C::SendRetransmission(PeerPtr(no)) => A::SendRetransmission(AppPeerPtr(no)),
|
C::SendRetransmission(PeerPtr(no)) => A::SendRetransmission(AppPeerPtr(no)),
|
||||||
@@ -1013,6 +1039,33 @@ impl AppServer {
|
|||||||
broker.process_poll()?;
|
broker.process_poll()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API poll
|
||||||
|
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
self.api_manager.poll(
|
||||||
|
&mut self.crypt,
|
||||||
|
self.mio_poll.registry(),
|
||||||
|
&mut self.mio_token_dispenser,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
pub fn add_api_connection(&mut self, connection: mio::net::UnixStream) -> std::io::Result<()> {
|
||||||
|
self.api_manager.add_connection(
|
||||||
|
connection,
|
||||||
|
self.mio_poll.registry(),
|
||||||
|
&mut self.mio_token_dispenser,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
pub fn add_api_listener(&mut self, listener: mio::net::UnixListener) -> std::io::Result<()> {
|
||||||
|
self.api_manager.add_listener(
|
||||||
|
listener,
|
||||||
|
self.mio_poll.registry(),
|
||||||
|
&mut self.mio_token_dispenser,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
rosenpass/src/bin/gen-ipc-msg-types.rs
Normal file
86
rosenpass/src/bin/gen-ipc-msg-types.rs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
use anyhow::{Context, Result};
|
||||||
|
use heck::ToShoutySnakeCase;
|
||||||
|
|
||||||
|
use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN};
|
||||||
|
|
||||||
|
fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN]> {
|
||||||
|
match values.split_first() {
|
||||||
|
Some((head, tail)) => calculate_hash_value(hd.mix(head.as_bytes())?, tail),
|
||||||
|
None => Ok(hd.into_value()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_literal(path: &[&str]) -> Result<()> {
|
||||||
|
let val = calculate_hash_value(HashDomain::zero(), path)?;
|
||||||
|
let (last, prefix) = path.split_last().context("developer error!")?;
|
||||||
|
let var_name = last.to_shouty_snake_case();
|
||||||
|
|
||||||
|
print!("// hash domain hash of: ");
|
||||||
|
for n in prefix.iter() {
|
||||||
|
print!("{n} -> ");
|
||||||
|
}
|
||||||
|
println!("{last}");
|
||||||
|
|
||||||
|
let c = hex::encode(val)
|
||||||
|
.chars()
|
||||||
|
.collect::<Vec<char>>()
|
||||||
|
.chunks_exact(4)
|
||||||
|
.map(|chunk| chunk.iter().collect::<String>())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
println!("const {var_name} : RawMsgType = RawMsgType::from_le_bytes(hex!(\"{} {} {} {} {} {} {} {}\"));",
|
||||||
|
c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum Tree {
|
||||||
|
Branch(String, Vec<Tree>),
|
||||||
|
Leaf(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tree {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Branch(name, _) => name,
|
||||||
|
Self::Leaf(name) => name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_code_inner(&self, prefix: &[&str]) -> Result<()> {
|
||||||
|
let mut path = prefix.to_owned();
|
||||||
|
path.push(self.name());
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Branch(_, ref children) => {
|
||||||
|
for c in children.iter() {
|
||||||
|
c.gen_code_inner(&path)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Leaf(_) => print_literal(&path)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_code(&self) -> Result<()> {
|
||||||
|
self.gen_code_inner(&[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let tree = Tree::Branch(
|
||||||
|
"Rosenpass IPC API".to_owned(),
|
||||||
|
vec![Tree::Branch(
|
||||||
|
"Rosenpass Protocol Server".to_owned(),
|
||||||
|
vec![
|
||||||
|
Tree::Leaf("Ping Request".to_owned()),
|
||||||
|
Tree::Leaf("Ping Response".to_owned()),
|
||||||
|
],
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("type RawMsgType = u128;");
|
||||||
|
println!();
|
||||||
|
tree.gen_code()
|
||||||
|
}
|
||||||
@@ -3,13 +3,11 @@ use clap::{Parser, Subcommand};
|
|||||||
use rosenpass_cipher_traits::Kem;
|
use rosenpass_cipher_traits::Kem;
|
||||||
use rosenpass_ciphers::kem::StaticKem;
|
use rosenpass_ciphers::kem::StaticKem;
|
||||||
use rosenpass_secret_memory::file::StoreSecret;
|
use rosenpass_secret_memory::file::StoreSecret;
|
||||||
use rosenpass_secret_memory::{
|
use rosenpass_util::file::{LoadValue, LoadValueB64, StoreValue};
|
||||||
secret_policy_try_use_memfd_secrets, secret_policy_use_only_malloc_secrets,
|
|
||||||
};
|
|
||||||
use rosenpass_util::file::{LoadValue, LoadValueB64};
|
|
||||||
use rosenpass_wireguard_broker::brokers::native_unix::{
|
use rosenpass_wireguard_broker::brokers::native_unix::{
|
||||||
NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError,
|
NativeUnixBroker, NativeUnixBrokerConfigBaseBuilder, NativeUnixBrokerConfigBaseBuilderError,
|
||||||
};
|
};
|
||||||
|
use std::ops::DerefMut;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::app_server::AppServerTest;
|
use crate::app_server::AppServerTest;
|
||||||
@@ -34,11 +32,21 @@ pub struct CliArgs {
|
|||||||
#[arg(short, long, group = "log-level")]
|
#[arg(short, long, group = "log-level")]
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
api: crate::api::cli::ApiCli,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: CliCommand,
|
pub command: CliCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliArgs {
|
impl CliArgs {
|
||||||
|
pub fn apply_to_config(&self, _cfg: &mut config::Rosenpass) -> anyhow::Result<()> {
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
self.api.apply_to_config(_cfg)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// returns the log level filter set by CLI args
|
/// returns the log level filter set by CLI args
|
||||||
/// returns `None` if the user did not specify any log level filter via CLI
|
/// returns `None` if the user did not specify any log level filter via CLI
|
||||||
///
|
///
|
||||||
@@ -151,21 +159,14 @@ pub enum CliCommand {
|
|||||||
Man,
|
Man,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliCommand {
|
impl CliArgs {
|
||||||
/// runs the command specified via CLI
|
/// runs the command specified via CLI
|
||||||
///
|
///
|
||||||
/// ## TODO
|
/// ## TODO
|
||||||
/// - This method consumes the [`CliCommand`] value. It might be wise to use a reference...
|
/// - This method consumes the [`CliCommand`] value. It might be wise to use a reference...
|
||||||
pub fn run(self, test_helpers: Option<AppServerTest>) -> anyhow::Result<()> {
|
pub fn run(self, test_helpers: Option<AppServerTest>) -> anyhow::Result<()> {
|
||||||
//Specify secret policy
|
|
||||||
|
|
||||||
#[cfg(feature = "enable_memfd_alloc")]
|
|
||||||
secret_policy_try_use_memfd_secrets();
|
|
||||||
#[cfg(not(feature = "enable_memfd_alloc"))]
|
|
||||||
secret_policy_use_only_malloc_secrets();
|
|
||||||
|
|
||||||
use CliCommand::*;
|
use CliCommand::*;
|
||||||
match self {
|
match &self.command {
|
||||||
Man => {
|
Man => {
|
||||||
let man_cmd = std::process::Command::new("man")
|
let man_cmd = std::process::Command::new("man")
|
||||||
.args(["1", "rosenpass"])
|
.args(["1", "rosenpass"])
|
||||||
@@ -177,7 +178,7 @@ impl CliCommand {
|
|||||||
}
|
}
|
||||||
GenConfig { config_file, force } => {
|
GenConfig { config_file, force } => {
|
||||||
ensure!(
|
ensure!(
|
||||||
force || !config_file.exists(),
|
*force || !config_file.exists(),
|
||||||
"config file {config_file:?} already exists"
|
"config file {config_file:?} already exists"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -192,9 +193,9 @@ impl CliCommand {
|
|||||||
let mut secret_key: Option<PathBuf> = None;
|
let mut secret_key: Option<PathBuf> = None;
|
||||||
|
|
||||||
// Manual arg parsing, since clap wants to prefix flags with "--"
|
// Manual arg parsing, since clap wants to prefix flags with "--"
|
||||||
let mut args = args.into_iter();
|
let mut args = args.iter();
|
||||||
loop {
|
loop {
|
||||||
match (args.next().as_deref(), args.next()) {
|
match (args.next().map(|x| x.as_str()), args.next()) {
|
||||||
(Some("private-key"), Some(opt)) | (Some("secret-key"), Some(opt)) => {
|
(Some("private-key"), Some(opt)) | (Some("secret-key"), Some(opt)) => {
|
||||||
secret_key = Some(opt.into());
|
secret_key = Some(opt.into());
|
||||||
}
|
}
|
||||||
@@ -236,7 +237,7 @@ impl CliCommand {
|
|||||||
|
|
||||||
(config.public_key, config.secret_key)
|
(config.public_key, config.secret_key)
|
||||||
}
|
}
|
||||||
(_, Some(pkf), Some(skf)) => (pkf, skf),
|
(_, Some(pkf), Some(skf)) => (pkf.clone(), skf.clone()),
|
||||||
_ => {
|
_ => {
|
||||||
bail!("either a config-file or both public-key and secret-key file are required")
|
bail!("either a config-file or both public-key and secret-key file are required")
|
||||||
}
|
}
|
||||||
@@ -268,31 +269,36 @@ impl CliCommand {
|
|||||||
"config file '{config_file:?}' does not exist"
|
"config file '{config_file:?}' does not exist"
|
||||||
);
|
);
|
||||||
|
|
||||||
let config = config::Rosenpass::load(config_file)?;
|
let mut config = config::Rosenpass::load(config_file)?;
|
||||||
config.validate()?;
|
config.validate()?;
|
||||||
|
self.apply_to_config(&mut config)?;
|
||||||
|
|
||||||
Self::event_loop(config, test_helpers)?;
|
Self::event_loop(config, test_helpers)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Exchange {
|
Exchange {
|
||||||
first_arg,
|
first_arg,
|
||||||
mut rest_of_args,
|
rest_of_args,
|
||||||
config_file,
|
config_file,
|
||||||
} => {
|
} => {
|
||||||
rest_of_args.insert(0, first_arg);
|
let mut rest_of_args = rest_of_args.clone();
|
||||||
|
rest_of_args.insert(0, first_arg.clone());
|
||||||
let args = rest_of_args;
|
let args = rest_of_args;
|
||||||
let mut config = config::Rosenpass::parse_args(args)?;
|
let mut config = config::Rosenpass::parse_args(args)?;
|
||||||
|
|
||||||
if let Some(p) = config_file {
|
if let Some(p) = config_file {
|
||||||
config.store(&p)?;
|
config.store(p)?;
|
||||||
config.config_file_path = p;
|
config.config_file_path.clone_from(p);
|
||||||
}
|
}
|
||||||
config.validate()?;
|
config.validate()?;
|
||||||
|
self.apply_to_config(&mut config)?;
|
||||||
|
|
||||||
Self::event_loop(config, test_helpers)?;
|
Self::event_loop(config, test_helpers)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Validate { config_files } => {
|
Validate { config_files } => {
|
||||||
for file in config_files {
|
for file in config_files {
|
||||||
match config::Rosenpass::load(&file) {
|
match config::Rosenpass::load(file) {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
eprintln!("{file:?} is valid TOML and conforms to the expected schema");
|
eprintln!("{file:?} is valid TOML and conforms to the expected schema");
|
||||||
match config.validate() {
|
match config.validate() {
|
||||||
@@ -314,6 +320,7 @@ impl CliCommand {
|
|||||||
test_helpers: Option<AppServerTest>,
|
test_helpers: Option<AppServerTest>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
const MAX_PSK_SIZE: usize = 1000;
|
const MAX_PSK_SIZE: usize = 1000;
|
||||||
|
|
||||||
// load own keys
|
// load own keys
|
||||||
let sk = SSk::load(&config.secret_key)?;
|
let sk = SSk::load(&config.secret_key)?;
|
||||||
let pk = SPk::load(&config.public_key)?;
|
let pk = SPk::load(&config.public_key)?;
|
||||||
@@ -322,11 +329,13 @@ impl CliCommand {
|
|||||||
let mut srv = std::boxed::Box::<AppServer>::new(AppServer::new(
|
let mut srv = std::boxed::Box::<AppServer>::new(AppServer::new(
|
||||||
sk,
|
sk,
|
||||||
pk,
|
pk,
|
||||||
config.listen,
|
config.listen.clone(),
|
||||||
config.verbosity,
|
config.verbosity,
|
||||||
test_helpers,
|
test_helpers,
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
|
config.apply_to_app_server(&mut srv)?;
|
||||||
|
|
||||||
let broker_store_ptr = srv.register_broker(Box::new(NativeUnixBroker::new()))?;
|
let broker_store_ptr = srv.register_broker(Box::new(NativeUnixBroker::new()))?;
|
||||||
|
|
||||||
fn cfg_err_map(e: NativeUnixBrokerConfigBaseBuilderError) -> anyhow::Error {
|
fn cfg_err_map(e: NativeUnixBrokerConfigBaseBuilderError) -> anyhow::Error {
|
||||||
@@ -370,7 +379,19 @@ impl CliCommand {
|
|||||||
fn generate_and_save_keypair(secret_key: PathBuf, public_key: PathBuf) -> anyhow::Result<()> {
|
fn generate_and_save_keypair(secret_key: PathBuf, public_key: PathBuf) -> anyhow::Result<()> {
|
||||||
let mut ssk = crate::protocol::SSk::random();
|
let mut ssk = crate::protocol::SSk::random();
|
||||||
let mut spk = crate::protocol::SPk::random();
|
let mut spk = crate::protocol::SPk::random();
|
||||||
StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
StaticKem::keygen(ssk.secret_mut(), spk.deref_mut())?;
|
||||||
ssk.store_secret(secret_key)?;
|
ssk.store_secret(secret_key)?;
|
||||||
spk.store(public_key)
|
spk.store(public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "internal_testing")]
|
||||||
|
pub mod testing {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn generate_and_save_keypair(
|
||||||
|
secret_key: PathBuf,
|
||||||
|
public_key: PathBuf,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
super::generate_and_save_keypair(secret_key, public_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ use anyhow::{bail, ensure};
|
|||||||
use rosenpass_util::file::{fopen_w, Visibility};
|
use rosenpass_util::file::{fopen_w, Visibility};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::app_server::AppServer;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Rosenpass {
|
pub struct Rosenpass {
|
||||||
/// path to the public key file
|
/// path to the public key file
|
||||||
@@ -27,6 +29,10 @@ pub struct Rosenpass {
|
|||||||
/// path to the secret key file
|
/// path to the secret key file
|
||||||
pub secret_key: PathBuf,
|
pub secret_key: PathBuf,
|
||||||
|
|
||||||
|
/// Location of the API listen sockets
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
pub api: crate::api::config::ApiConfig,
|
||||||
|
|
||||||
/// list of [`SocketAddr`] to listen on
|
/// list of [`SocketAddr`] to listen on
|
||||||
///
|
///
|
||||||
/// Examples:
|
/// Examples:
|
||||||
@@ -54,7 +60,7 @@ pub struct Rosenpass {
|
|||||||
|
|
||||||
/// ## TODO
|
/// ## TODO
|
||||||
/// - replace this type with [`log::LevelFilter`], also see <https://github.com/rosenpass/rosenpass/pull/246>
|
/// - replace this type with [`log::LevelFilter`], also see <https://github.com/rosenpass/rosenpass/pull/246>
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone)]
|
||||||
pub enum Verbosity {
|
pub enum Verbosity {
|
||||||
Quiet,
|
Quiet,
|
||||||
Verbose,
|
Verbose,
|
||||||
@@ -157,6 +163,12 @@ impl Rosenpass {
|
|||||||
self.store(&self.config_file_path)
|
self.store(&self.config_file_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn apply_to_app_server(&self, _srv: &mut AppServer) -> anyhow::Result<()> {
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
self.api.apply_to_app_server(_srv)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Validate a configuration
|
/// Validate a configuration
|
||||||
///
|
///
|
||||||
/// ## TODO
|
/// ## TODO
|
||||||
@@ -206,6 +218,8 @@ impl Rosenpass {
|
|||||||
public_key: PathBuf::from(public_key.as_ref()),
|
public_key: PathBuf::from(public_key.as_ref()),
|
||||||
secret_key: PathBuf::from(secret_key.as_ref()),
|
secret_key: PathBuf::from(secret_key.as_ref()),
|
||||||
listen: vec![],
|
listen: vec![],
|
||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
api: crate::api::config::ApiConfig::default(),
|
||||||
verbosity: Verbosity::Quiet,
|
verbosity: Verbosity::Quiet,
|
||||||
peers: vec![],
|
peers: vec![],
|
||||||
config_file_path: PathBuf::new(),
|
config_file_path: PathBuf::new(),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#[cfg(feature = "experiment_api")]
|
||||||
|
pub mod api;
|
||||||
pub mod app_server;
|
pub mod app_server;
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
@@ -11,4 +13,8 @@ pub enum RosenpassError {
|
|||||||
BufferSizeMismatch,
|
BufferSizeMismatch,
|
||||||
#[error("invalid message type")]
|
#[error("invalid message type")]
|
||||||
InvalidMessageType(u8),
|
InvalidMessageType(u8),
|
||||||
|
#[error("invalid API message type")]
|
||||||
|
InvalidApiMessageType(u128),
|
||||||
|
#[error("could not parse API message")]
|
||||||
|
InvalidApiMessage,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ pub fn main() {
|
|||||||
// parse CLI arguments
|
// parse CLI arguments
|
||||||
let args = CliArgs::parse();
|
let args = CliArgs::parse();
|
||||||
|
|
||||||
|
{
|
||||||
|
use rosenpass_secret_memory as SM;
|
||||||
|
#[cfg(feature = "experiment_memfd_secret")]
|
||||||
|
SM::secret_policy_try_use_memfd_secrets();
|
||||||
|
#[cfg(not(feature = "experiment_memfd_secret"))]
|
||||||
|
SM::secret_policy_use_only_malloc_secrets();
|
||||||
|
}
|
||||||
|
|
||||||
// init logging
|
// init logging
|
||||||
{
|
{
|
||||||
let mut log_builder = env_logger::Builder::from_default_env(); // sets log level filter from environment (or defaults)
|
let mut log_builder = env_logger::Builder::from_default_env(); // sets log level filter from environment (or defaults)
|
||||||
@@ -26,10 +34,10 @@ pub fn main() {
|
|||||||
// error!("error dummy");
|
// error!("error dummy");
|
||||||
}
|
}
|
||||||
|
|
||||||
match args.command.run(None) {
|
match args.run(None) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{e}");
|
error!("{e:?}");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
//! [CryptoServer].
|
//! [CryptoServer].
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
|
//! use std::ops::DerefMut;
|
||||||
//! use rosenpass_secret_memory::policy::*;
|
//! use rosenpass_secret_memory::policy::*;
|
||||||
//! use rosenpass_cipher_traits::Kem;
|
//! use rosenpass_cipher_traits::Kem;
|
||||||
//! use rosenpass_ciphers::kem::StaticKem;
|
//! use rosenpass_ciphers::kem::StaticKem;
|
||||||
@@ -32,11 +33,11 @@
|
|||||||
//!
|
//!
|
||||||
//! // initialize secret and public key for peer a ...
|
//! // initialize secret and public key for peer a ...
|
||||||
//! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero());
|
//! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero());
|
||||||
//! StaticKem::keygen(peer_a_sk.secret_mut(), peer_a_pk.secret_mut())?;
|
//! StaticKem::keygen(peer_a_sk.secret_mut(), peer_a_pk.deref_mut())?;
|
||||||
//!
|
//!
|
||||||
//! // ... and for peer b
|
//! // ... and for peer b
|
||||||
//! let (mut peer_b_sk, mut peer_b_pk) = (SSk::zero(), SPk::zero());
|
//! let (mut peer_b_sk, mut peer_b_pk) = (SSk::zero(), SPk::zero());
|
||||||
//! StaticKem::keygen(peer_b_sk.secret_mut(), peer_b_pk.secret_mut())?;
|
//! StaticKem::keygen(peer_b_sk.secret_mut(), peer_b_pk.deref_mut())?;
|
||||||
//!
|
//!
|
||||||
//! // initialize server and a pre-shared key
|
//! // initialize server and a pre-shared key
|
||||||
//! let psk = SymKey::random();
|
//! let psk = SymKey::random();
|
||||||
@@ -71,6 +72,7 @@
|
|||||||
|
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::{
|
use std::{
|
||||||
collections::hash_map::{
|
collections::hash_map::{
|
||||||
Entry::{Occupied, Vacant},
|
Entry::{Occupied, Vacant},
|
||||||
@@ -88,7 +90,7 @@ use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace
|
|||||||
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
|
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
|
||||||
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
||||||
use rosenpass_constant_time as constant_time;
|
use rosenpass_constant_time as constant_time;
|
||||||
use rosenpass_secret_memory::{Public, Secret};
|
use rosenpass_secret_memory::{Public, PublicBox, Secret};
|
||||||
use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase};
|
use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase};
|
||||||
use zerocopy::{AsBytes, FromBytes, Ref};
|
use zerocopy::{AsBytes, FromBytes, Ref};
|
||||||
|
|
||||||
@@ -163,7 +165,7 @@ pub fn has_happened(ev: Timing, now: Timing) -> bool {
|
|||||||
|
|
||||||
// DATA STRUCTURES & BASIC TRAITS & ACCESSORS ////
|
// DATA STRUCTURES & BASIC TRAITS & ACCESSORS ////
|
||||||
|
|
||||||
pub type SPk = Secret<{ StaticKem::PK_LEN }>; // Just Secret<> instead of Public<> so it gets allocated on the heap
|
pub type SPk = PublicBox<{ StaticKem::PK_LEN }>;
|
||||||
pub type SSk = Secret<{ StaticKem::SK_LEN }>;
|
pub type SSk = Secret<{ StaticKem::SK_LEN }>;
|
||||||
pub type EPk = Public<{ EphemeralKem::PK_LEN }>;
|
pub type EPk = Public<{ EphemeralKem::PK_LEN }>;
|
||||||
pub type ESk = Secret<{ EphemeralKem::SK_LEN }>;
|
pub type ESk = Secret<{ EphemeralKem::SK_LEN }>;
|
||||||
@@ -548,7 +550,7 @@ impl CryptoServer {
|
|||||||
pub fn pidm(&self) -> Result<PeerId> {
|
pub fn pidm(&self) -> Result<PeerId> {
|
||||||
Ok(Public::new(
|
Ok(Public::new(
|
||||||
hash_domains::peerid()?
|
hash_domains::peerid()?
|
||||||
.mix(self.spkm.secret())?
|
.mix(self.spkm.deref())?
|
||||||
.into_value()))
|
.into_value()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,7 +710,7 @@ impl Peer {
|
|||||||
pub fn pidt(&self) -> Result<PeerId> {
|
pub fn pidt(&self) -> Result<PeerId> {
|
||||||
Ok(Public::new(
|
Ok(Public::new(
|
||||||
hash_domains::peerid()?
|
hash_domains::peerid()?
|
||||||
.mix(self.spkt.secret())?
|
.mix(self.spkt.deref())?
|
||||||
.into_value()))
|
.into_value()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1017,7 +1019,7 @@ impl CryptoServer {
|
|||||||
|
|
||||||
let cookie_value = active_cookie_value.unwrap();
|
let cookie_value = active_cookie_value.unwrap();
|
||||||
let cookie_key = hash_domains::cookie_key()?
|
let cookie_key = hash_domains::cookie_key()?
|
||||||
.mix(self.spkm.secret())?
|
.mix(self.spkm.deref())?
|
||||||
.into_value();
|
.into_value();
|
||||||
|
|
||||||
let mut msg_out = truncating_cast_into::<CookieReply>(tx_buf)?;
|
let mut msg_out = truncating_cast_into::<CookieReply>(tx_buf)?;
|
||||||
@@ -1509,7 +1511,7 @@ where
|
|||||||
/// Calculate the message authentication code (`mac`) and also append cookie value
|
/// Calculate the message authentication code (`mac`) and also append cookie value
|
||||||
pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> {
|
pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> {
|
||||||
let mac = hash_domains::mac()?
|
let mac = hash_domains::mac()?
|
||||||
.mix(peer.get(srv).spkt.secret())?
|
.mix(peer.get(srv).spkt.deref())?
|
||||||
.mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?;
|
.mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?;
|
||||||
self.mac.copy_from_slice(mac.into_value()[..16].as_ref());
|
self.mac.copy_from_slice(mac.into_value()[..16].as_ref());
|
||||||
self.seal_cookie(peer, srv)?;
|
self.seal_cookie(peer, srv)?;
|
||||||
@@ -1536,7 +1538,7 @@ where
|
|||||||
/// Check the message authentication code
|
/// Check the message authentication code
|
||||||
pub fn check_seal(&self, srv: &CryptoServer) -> Result<bool> {
|
pub fn check_seal(&self, srv: &CryptoServer) -> Result<bool> {
|
||||||
let expected = hash_domains::mac()?
|
let expected = hash_domains::mac()?
|
||||||
.mix(srv.spkm.secret())?
|
.mix(srv.spkm.deref())?
|
||||||
.mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?;
|
.mix(&self.as_bytes()[span_of!(Self, msg_type..mac)])?;
|
||||||
Ok(constant_time::memcmp(
|
Ok(constant_time::memcmp(
|
||||||
&self.mac,
|
&self.mac,
|
||||||
@@ -1641,7 +1643,7 @@ impl HandshakeState {
|
|||||||
|
|
||||||
// calculate ad contents
|
// calculate ad contents
|
||||||
let ad = hash_domains::biscuit_ad()?
|
let ad = hash_domains::biscuit_ad()?
|
||||||
.mix(srv.spkm.secret())?
|
.mix(srv.spkm.deref())?
|
||||||
.mix(self.sidi.as_slice())?
|
.mix(self.sidi.as_slice())?
|
||||||
.mix(self.sidr.as_slice())?
|
.mix(self.sidr.as_slice())?
|
||||||
.into_value();
|
.into_value();
|
||||||
@@ -1676,7 +1678,7 @@ impl HandshakeState {
|
|||||||
|
|
||||||
// Calculate additional data fields
|
// Calculate additional data fields
|
||||||
let ad = hash_domains::biscuit_ad()?
|
let ad = hash_domains::biscuit_ad()?
|
||||||
.mix(srv.spkm.secret())?
|
.mix(srv.spkm.deref())?
|
||||||
.mix(sidi.as_slice())?
|
.mix(sidi.as_slice())?
|
||||||
.mix(sidr.as_slice())?
|
.mix(sidr.as_slice())?
|
||||||
.into_value();
|
.into_value();
|
||||||
@@ -1763,7 +1765,7 @@ impl CryptoServer {
|
|||||||
let mut hs = InitiatorHandshake::zero_with_timestamp(self);
|
let mut hs = InitiatorHandshake::zero_with_timestamp(self);
|
||||||
|
|
||||||
// IHI1
|
// IHI1
|
||||||
hs.core.init(peer.get(self).spkt.secret())?;
|
hs.core.init(peer.get(self).spkt.deref())?;
|
||||||
|
|
||||||
// IHI2
|
// IHI2
|
||||||
hs.core.sidi.randomize();
|
hs.core.sidi.randomize();
|
||||||
@@ -1780,7 +1782,7 @@ impl CryptoServer {
|
|||||||
hs.core
|
hs.core
|
||||||
.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||||
ih.sctr.as_mut_slice(),
|
ih.sctr.as_mut_slice(),
|
||||||
peer.get(self).spkt.secret(),
|
peer.get(self).spkt.deref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// IHI6
|
// IHI6
|
||||||
@@ -1789,7 +1791,7 @@ impl CryptoServer {
|
|||||||
|
|
||||||
// IHI7
|
// IHI7
|
||||||
hs.core
|
hs.core
|
||||||
.mix(self.spkm.secret())?
|
.mix(self.spkm.deref())?
|
||||||
.mix(peer.get(self).psk.secret())?;
|
.mix(peer.get(self).psk.secret())?;
|
||||||
|
|
||||||
// IHI8
|
// IHI8
|
||||||
@@ -1807,7 +1809,7 @@ impl CryptoServer {
|
|||||||
core.sidi = SessionId::from_slice(&ih.sidi);
|
core.sidi = SessionId::from_slice(&ih.sidi);
|
||||||
|
|
||||||
// IHR1
|
// IHR1
|
||||||
core.init(self.spkm.secret())?;
|
core.init(self.spkm.deref())?;
|
||||||
|
|
||||||
// IHR4
|
// IHR4
|
||||||
core.mix(&ih.sidi)?.mix(&ih.epki)?;
|
core.mix(&ih.sidi)?.mix(&ih.epki)?;
|
||||||
@@ -1815,7 +1817,7 @@ impl CryptoServer {
|
|||||||
// IHR5
|
// IHR5
|
||||||
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||||
self.sskm.secret(),
|
self.sskm.secret(),
|
||||||
self.spkm.secret(),
|
self.spkm.deref(),
|
||||||
&ih.sctr,
|
&ih.sctr,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -1828,7 +1830,7 @@ impl CryptoServer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// IHR7
|
// IHR7
|
||||||
core.mix(peer.get(self).spkt.secret())?
|
core.mix(peer.get(self).spkt.deref())?
|
||||||
.mix(peer.get(self).psk.secret())?;
|
.mix(peer.get(self).psk.secret())?;
|
||||||
|
|
||||||
// IHR8
|
// IHR8
|
||||||
@@ -1848,7 +1850,7 @@ impl CryptoServer {
|
|||||||
// RHR5
|
// RHR5
|
||||||
core.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
core.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||||
&mut rh.scti,
|
&mut rh.scti,
|
||||||
peer.get(self).spkt.secret(),
|
peer.get(self).spkt.deref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// RHR6
|
// RHR6
|
||||||
@@ -1909,14 +1911,14 @@ impl CryptoServer {
|
|||||||
// RHI4
|
// RHI4
|
||||||
core.decaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(
|
core.decaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(
|
||||||
hs!().eski.secret(),
|
hs!().eski.secret(),
|
||||||
&*hs!().epki,
|
hs!().epki.deref(),
|
||||||
&rh.ecti,
|
&rh.ecti,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// RHI5
|
// RHI5
|
||||||
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||||
self.sskm.secret(),
|
self.sskm.secret(),
|
||||||
self.spkm.secret(),
|
self.spkm.deref(),
|
||||||
&rh.scti,
|
&rh.scti,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -2113,7 +2115,7 @@ impl CryptoServer {
|
|||||||
),
|
),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let spkt = peer.get(self).spkt.secret();
|
let spkt = peer.get(self).spkt.deref();
|
||||||
let cookie_key = hash_domains::cookie_key()?.mix(spkt)?.into_value();
|
let cookie_key = hash_domains::cookie_key()?.mix(spkt)?.into_value();
|
||||||
let cookie_value = peer.cv().update_mut(self).unwrap();
|
let cookie_value = peer.cv().update_mut(self).unwrap();
|
||||||
|
|
||||||
@@ -2146,7 +2148,7 @@ fn truncating_cast_into_nomut<T: FromBytes>(buf: &[u8]) -> Result<Ref<&[u8], T>,
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::{net::SocketAddrV4, thread::sleep, time::Duration};
|
use std::{net::SocketAddrV4, ops::DerefMut, thread::sleep, time::Duration};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
@@ -2255,7 +2257,7 @@ mod test {
|
|||||||
fn keygen() -> Result<(SSk, SPk)> {
|
fn keygen() -> Result<(SSk, SPk)> {
|
||||||
// TODO: Copied from the benchmark; deduplicate
|
// TODO: Copied from the benchmark; deduplicate
|
||||||
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||||
StaticKem::keygen(sk.secret_mut(), pk.secret_mut())?;
|
StaticKem::keygen(sk.secret_mut(), pk.deref_mut())?;
|
||||||
Ok((sk, pk))
|
Ok((sk, pk))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
180
rosenpass/tests/api-integration-tests.rs
Normal file
180
rosenpass/tests/api-integration-tests.rs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
use std::{
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
net::ToSocketAddrs,
|
||||||
|
os::unix::net::UnixStream,
|
||||||
|
process::Stdio,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{bail, Context};
|
||||||
|
use rosenpass::api;
|
||||||
|
use rosenpass_to::{ops::copy_slice_least_src, To};
|
||||||
|
use rosenpass_util::zerocopy::ZerocopySliceExt;
|
||||||
|
use rosenpass_util::{
|
||||||
|
file::LoadValueB64,
|
||||||
|
length_prefix_encoding::{decoder::LengthPrefixDecoder, encoder::LengthPrefixEncoder},
|
||||||
|
};
|
||||||
|
use tempfile::TempDir;
|
||||||
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
|
use rosenpass::protocol::SymKey;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn api_integration_test() -> anyhow::Result<()> {
|
||||||
|
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();
|
||||||
|
|
||||||
|
let dir = TempDir::with_prefix("rosenpass-api-integration-test")?;
|
||||||
|
|
||||||
|
macro_rules! tempfile {
|
||||||
|
($($lst:expr),+) => {{
|
||||||
|
let mut buf = dir.path().to_path_buf();
|
||||||
|
$(buf.push($lst);)*
|
||||||
|
buf
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
let peer_a_endpoint = "[::1]:61423";
|
||||||
|
let peer_a_osk = tempfile!("a.osk");
|
||||||
|
let peer_b_osk = tempfile!("b.osk");
|
||||||
|
|
||||||
|
use rosenpass::config;
|
||||||
|
let peer_a = config::Rosenpass {
|
||||||
|
config_file_path: tempfile!("a.config"),
|
||||||
|
secret_key: tempfile!("a.sk"),
|
||||||
|
public_key: tempfile!("a.pk"),
|
||||||
|
listen: peer_a_endpoint.to_socket_addrs()?.collect(), // TODO: This could collide by accident
|
||||||
|
verbosity: config::Verbosity::Verbose,
|
||||||
|
api: api::config::ApiConfig {
|
||||||
|
listen_path: vec![tempfile!("a.sock")],
|
||||||
|
listen_fd: vec![],
|
||||||
|
stream_fd: vec![],
|
||||||
|
},
|
||||||
|
peers: vec![config::RosenpassPeer {
|
||||||
|
public_key: tempfile!("b.pk"),
|
||||||
|
key_out: Some(peer_a_osk.clone()),
|
||||||
|
endpoint: None,
|
||||||
|
pre_shared_key: None,
|
||||||
|
wg: None,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
let peer_b = config::Rosenpass {
|
||||||
|
config_file_path: tempfile!("b.config"),
|
||||||
|
secret_key: tempfile!("b.sk"),
|
||||||
|
public_key: tempfile!("b.pk"),
|
||||||
|
listen: vec![],
|
||||||
|
verbosity: config::Verbosity::Verbose,
|
||||||
|
api: api::config::ApiConfig {
|
||||||
|
listen_path: vec![tempfile!("b.sock")],
|
||||||
|
listen_fd: vec![],
|
||||||
|
stream_fd: vec![],
|
||||||
|
},
|
||||||
|
peers: vec![config::RosenpassPeer {
|
||||||
|
public_key: tempfile!("a.pk"),
|
||||||
|
key_out: Some(peer_b_osk.clone()),
|
||||||
|
endpoint: Some(peer_a_endpoint.to_owned()),
|
||||||
|
pre_shared_key: None,
|
||||||
|
wg: None,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate the keys
|
||||||
|
rosenpass::cli::testing::generate_and_save_keypair(
|
||||||
|
peer_a.secret_key.clone(),
|
||||||
|
peer_a.public_key.clone(),
|
||||||
|
)?;
|
||||||
|
rosenpass::cli::testing::generate_and_save_keypair(
|
||||||
|
peer_b.secret_key.clone(),
|
||||||
|
peer_b.public_key.clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Write the configuration files
|
||||||
|
peer_a.commit()?;
|
||||||
|
peer_b.commit()?;
|
||||||
|
|
||||||
|
// Start peer a
|
||||||
|
let proc_a = std::process::Command::new(env!("CARGO_BIN_EXE_rosenpass"))
|
||||||
|
.args([
|
||||||
|
"exchange-config",
|
||||||
|
peer_a.config_file_path.to_str().context("")?,
|
||||||
|
])
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
// Start peer b
|
||||||
|
let proc_b = std::process::Command::new(env!("CARGO_BIN_EXE_rosenpass"))
|
||||||
|
.args([
|
||||||
|
"exchange-config",
|
||||||
|
peer_b.config_file_path.to_str().context("")?,
|
||||||
|
])
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
// Acquire stdout
|
||||||
|
let mut out_a = BufReader::new(proc_a.stdout.context("")?).lines();
|
||||||
|
let mut out_b = BufReader::new(proc_b.stdout.context("")?).lines();
|
||||||
|
|
||||||
|
// Wait for the keys to successfully exchange a key
|
||||||
|
let mut attempt = 0;
|
||||||
|
loop {
|
||||||
|
let line_a = out_a.next().context("")??;
|
||||||
|
let line_b = out_b.next().context("")??;
|
||||||
|
|
||||||
|
let words_a = line_a.split(' ').collect::<Vec<_>>();
|
||||||
|
let words_b = line_b.split(' ').collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// FIXED FIXED PEER-ID FIXED FILENAME STATUS
|
||||||
|
// output-key peer KZqXTZ4l2aNnkJtLPhs4D8JxHTGmRSL9w3Qr+X8JxFk= key-file "client-A-osk" exchanged
|
||||||
|
let peer_a_id = words_b
|
||||||
|
.get(2)
|
||||||
|
.with_context(|| format!("Bad rosenpass output: `{line_b}`"))?;
|
||||||
|
let peer_b_id = words_a
|
||||||
|
.get(2)
|
||||||
|
.with_context(|| format!("Bad rosenpass output: `{line_a}`"))?;
|
||||||
|
assert_eq!(
|
||||||
|
line_a,
|
||||||
|
format!(
|
||||||
|
"output-key peer {peer_b_id} key-file \"{}\" exchanged",
|
||||||
|
peer_a_osk.to_str().context("")?
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
line_b,
|
||||||
|
format!(
|
||||||
|
"output-key peer {peer_a_id} key-file \"{}\" exchanged",
|
||||||
|
peer_b_osk.to_str().context("")?
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Read OSKs
|
||||||
|
let osk_a = SymKey::load_b64::<64, _>(peer_a_osk.clone())?;
|
||||||
|
let osk_b = SymKey::load_b64::<64, _>(peer_b_osk.clone())?;
|
||||||
|
match osk_a.secret() == osk_b.secret() {
|
||||||
|
true => break,
|
||||||
|
false if attempt > 10 => bail!("Peers did not produce a matching key even after ten attempts. Something is wrong with the key exchange!"),
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
attempt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now connect to the peers
|
||||||
|
let api_a = UnixStream::connect(&peer_a.api.listen_path[0])?;
|
||||||
|
let api_b = UnixStream::connect(&peer_b.api.listen_path[0])?;
|
||||||
|
|
||||||
|
for conn in ([api_a, api_b]).iter() {
|
||||||
|
let mut echo = [0u8; 256];
|
||||||
|
copy_slice_least_src("Hello World".as_bytes()).to(&mut echo);
|
||||||
|
|
||||||
|
let req = api::PingRequest::new(echo);
|
||||||
|
LengthPrefixEncoder::from_message(req.as_bytes()).write_all_to_stdio(conn)?;
|
||||||
|
|
||||||
|
let mut decoder = LengthPrefixDecoder::new([0u8; api::MAX_RESPONSE_LEN]);
|
||||||
|
let res = decoder.read_all_from_stdio(conn)?;
|
||||||
|
let res = res.zk_parse::<api::PingResponse>()?;
|
||||||
|
assert_eq!(*res, api::PingResponse::new(echo));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -15,9 +15,19 @@ use std::io::Write;
|
|||||||
|
|
||||||
const BIN: &str = "rosenpass";
|
const BIN: &str = "rosenpass";
|
||||||
|
|
||||||
|
fn setup_tests() {
|
||||||
|
use rosenpass_secret_memory as SM;
|
||||||
|
#[cfg(feature = "experiment_memfd_secret")]
|
||||||
|
SM::secret_policy_try_use_memfd_secrets();
|
||||||
|
#[cfg(not(feature = "experiment_memfd_secret"))]
|
||||||
|
SM::secret_policy_use_only_malloc_secrets();
|
||||||
|
}
|
||||||
|
|
||||||
// check that we can generate keys
|
// check that we can generate keys
|
||||||
#[test]
|
#[test]
|
||||||
fn generate_keys() {
|
fn generate_keys() {
|
||||||
|
setup_tests();
|
||||||
|
|
||||||
let tmpdir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("keygen");
|
let tmpdir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("keygen");
|
||||||
fs::create_dir_all(&tmpdir).unwrap();
|
fs::create_dir_all(&tmpdir).unwrap();
|
||||||
|
|
||||||
@@ -94,14 +104,11 @@ fn run_server_client_exchange(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
cli.command
|
let test_helpers = server_test_builder
|
||||||
.run(Some(
|
.termination_handler(Some(server_terminate_rx))
|
||||||
server_test_builder
|
.build()
|
||||||
.termination_handler(Some(server_terminate_rx))
|
|
||||||
.build()
|
|
||||||
.unwrap(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
cli.run(Some(test_helpers)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
let cli = CliArgs::try_parse_from(
|
let cli = CliArgs::try_parse_from(
|
||||||
@@ -112,14 +119,11 @@ fn run_server_client_exchange(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
cli.command
|
let test_helpers = client_test_builder
|
||||||
.run(Some(
|
.termination_handler(Some(client_terminate_rx))
|
||||||
client_test_builder
|
.build()
|
||||||
.termination_handler(Some(client_terminate_rx))
|
|
||||||
.build()
|
|
||||||
.unwrap(),
|
|
||||||
))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
cli.run(Some(test_helpers)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
// give them some time to do the key exchange under load
|
// give them some time to do the key exchange under load
|
||||||
@@ -134,6 +138,7 @@ fn run_server_client_exchange(
|
|||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn check_exchange_under_normal() {
|
fn check_exchange_under_normal() {
|
||||||
|
setup_tests();
|
||||||
setup_logging();
|
setup_logging();
|
||||||
|
|
||||||
let tmpdir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("exchange");
|
let tmpdir = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("exchange");
|
||||||
@@ -206,6 +211,7 @@ fn check_exchange_under_normal() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn check_exchange_under_dos() {
|
fn check_exchange_under_dos() {
|
||||||
|
setup_tests();
|
||||||
setup_logging();
|
setup_logging();
|
||||||
|
|
||||||
//Generate binary with responder with feature integration_test
|
//Generate binary with responder with feature integration_test
|
||||||
@@ -283,6 +289,7 @@ struct MockBrokerInner {
|
|||||||
interface: Option<String>,
|
interface: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct MockBroker {
|
struct MockBroker {
|
||||||
inner: Arc<Mutex<MockBrokerInner>>,
|
inner: Arc<Mutex<MockBrokerInner>>,
|
||||||
@@ -321,7 +328,7 @@ impl rosenpass_wireguard_broker::WireGuardBroker for MockBroker {
|
|||||||
if let Ok(ref mut mutex) = lock {
|
if let Ok(ref mut mutex) = lock {
|
||||||
**mutex = MockBrokerInner {
|
**mutex = MockBrokerInner {
|
||||||
psk: Some(config.psk.clone()),
|
psk: Some(config.psk.clone()),
|
||||||
peer_id: Some(config.peer_id.clone()),
|
peer_id: Some(*config.peer_id),
|
||||||
interface: Some(std::str::from_utf8(config.interface).unwrap().to_string()),
|
interface: Some(std::str::from_utf8(config.interface).unwrap().to_string()),
|
||||||
};
|
};
|
||||||
break Ok(());
|
break Ok(());
|
||||||
|
|||||||
@@ -39,4 +39,5 @@ tempfile = {workspace = true}
|
|||||||
stacker = {workspace = true}
|
stacker = {workspace = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
enable_memfd_alloc = []
|
experiment_memfd_secret = []
|
||||||
|
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"]
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ use std::{net::SocketAddr, path::PathBuf};
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||||
use crate::key::WG_B64_LEN;
|
use crate::key::WG_B64_LEN;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ExchangePeer {
|
pub struct ExchangePeer {
|
||||||
pub public_keys_dir: PathBuf,
|
pub public_keys_dir: PathBuf,
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs::{self, DirBuilder},
|
fs::{self, DirBuilder},
|
||||||
|
ops::DerefMut,
|
||||||
os::unix::fs::{DirBuilderExt, PermissionsExt},
|
os::unix::fs::{DirBuilderExt, PermissionsExt},
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use rosenpass_util::file::{LoadValueB64, StoreValueB64};
|
use rosenpass_util::file::{LoadValueB64, StoreValue, StoreValueB64};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use rosenpass::protocol::{SPk, SSk};
|
use rosenpass::protocol::{SPk, SSk};
|
||||||
@@ -56,8 +57,8 @@ pub fn genkey(private_keys_dir: &Path) -> Result<()> {
|
|||||||
if !pqsk_path.exists() && !pqpk_path.exists() {
|
if !pqsk_path.exists() && !pqpk_path.exists() {
|
||||||
let mut pqsk = SSk::random();
|
let mut pqsk = SSk::random();
|
||||||
let mut pqpk = SPk::random();
|
let mut pqpk = SPk::random();
|
||||||
StaticKem::keygen(pqsk.secret_mut(), pqpk.secret_mut())?;
|
StaticKem::keygen(pqsk.secret_mut(), pqpk.deref_mut())?;
|
||||||
pqpk.store_secret(pqpk_path)?;
|
pqpk.store(pqpk_path)?;
|
||||||
pqsk.store_secret(pqsk_path)?;
|
pqsk.store_secret(pqsk_path)?;
|
||||||
} else {
|
} else {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ mod key;
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
#[cfg(feature = "enable_memfd_alloc")]
|
#[cfg(feature = "experiment_memfd_secret")]
|
||||||
policy::secret_policy_try_use_memfd_secrets();
|
policy::secret_policy_try_use_memfd_secrets();
|
||||||
#[cfg(not(feature = "enable_memfd_alloc"))]
|
#[cfg(not(feature = "experiment_memfd_secret"))]
|
||||||
policy::secret_policy_use_only_malloc_secrets();
|
policy::secret_policy_use_only_malloc_secrets();
|
||||||
|
|
||||||
let cli = match Cli::parse(std::env::args().peekable()) {
|
let cli = match Cli::parse(std::env::args().peekable()) {
|
||||||
|
|||||||
@@ -23,4 +23,4 @@ log = { workspace = true }
|
|||||||
allocator-api2-tests = { workspace = true }
|
allocator-api2-tests = { workspace = true }
|
||||||
tempfile = {workspace = true}
|
tempfile = {workspace = true}
|
||||||
base64ct = {workspace = true}
|
base64ct = {workspace = true}
|
||||||
procspawn = {workspace = true}
|
procspawn = {workspace = true}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ pub mod alloc;
|
|||||||
|
|
||||||
mod public;
|
mod public;
|
||||||
pub use crate::public::Public;
|
pub use crate::public::Public;
|
||||||
|
pub use crate::public::PublicBox;
|
||||||
|
|
||||||
mod secret;
|
mod secret;
|
||||||
pub use crate::secret::Secret;
|
pub use crate::secret::Secret;
|
||||||
|
|||||||
@@ -172,12 +172,154 @@ impl<const N: usize> StoreValueB64Writer for Public<N> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct PublicBox<const N: usize> {
|
||||||
|
pub inner: Box<Public<N>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> PublicBox<N> {
|
||||||
|
/// Create a new [PublicBox] from a byte slice
|
||||||
|
pub fn from_slice(value: &[u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::new(Public::from_slice(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new [PublicBox] from a byte array
|
||||||
|
pub fn new(value: [u8; N]) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::new(Public::new(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a zero initialized [PublicBox]
|
||||||
|
pub fn zero() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::new(Public::zero()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a random initialized [PublicBox]
|
||||||
|
pub fn random() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::new(Public::random()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Randomize all bytes in an existing [PublicBox]
|
||||||
|
pub fn randomize(&mut self) {
|
||||||
|
self.inner.randomize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Randomize for PublicBox<N> {
|
||||||
|
fn try_fill<R: Rng + ?Sized>(&mut self, rng: &mut R) -> Result<(), rand::Error> {
|
||||||
|
self.inner.try_fill(rng)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> fmt::Debug for PublicBox<N> {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
debug_crypto_array(&**self, fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Deref for PublicBox<N> {
|
||||||
|
type Target = [u8; N];
|
||||||
|
|
||||||
|
fn deref(&self) -> &[u8; N] {
|
||||||
|
self.inner.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> DerefMut for PublicBox<N> {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8; N] {
|
||||||
|
self.inner.deref_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Borrow<[u8]> for PublicBox<N> {
|
||||||
|
fn borrow(&self) -> &[u8] {
|
||||||
|
self.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> BorrowMut<[u8]> for PublicBox<N> {
|
||||||
|
fn borrow_mut(&mut self) -> &mut [u8] {
|
||||||
|
self.deref_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> LoadValue for PublicBox<N> {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
// This is implemented separately from Public to avoid allocating too much stack memory
|
||||||
|
fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
||||||
|
let mut p = Self::random();
|
||||||
|
fopen_r(path)?.read_exact_to_end(p.deref_mut())?;
|
||||||
|
Ok(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> StoreValue for PublicBox<N> {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn store<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||||
|
self.inner.store(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> LoadValueB64 for PublicBox<N> {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
// This is implemented separately from Public to avoid allocating too much stack memory
|
||||||
|
fn load_b64<const F: usize, P: AsRef<Path>>(path: P) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
// A vector is used here to ensure heap allocation without copy from stack
|
||||||
|
let mut f = vec![0u8; F];
|
||||||
|
let mut v = PublicBox::zero();
|
||||||
|
let p = path.as_ref();
|
||||||
|
|
||||||
|
let len = fopen_r(p)?
|
||||||
|
.read_slice_to_end(&mut f)
|
||||||
|
.with_context(|| format!("Could not load file {p:?}"))?;
|
||||||
|
|
||||||
|
b64_decode(&f[0..len], v.deref_mut())
|
||||||
|
.with_context(|| format!("Could not decode base64 file {p:?}"))?;
|
||||||
|
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> StoreValueB64 for PublicBox<N> {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn store_b64<const F: usize, P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||||
|
self.inner.store_b64::<F, P>(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> StoreValueB64Writer for PublicBox<N> {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn store_b64_writer<const F: usize, W: std::io::Write>(
|
||||||
|
&self,
|
||||||
|
writer: W,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.inner.store_b64_writer::<F, W>(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[allow(clippy::module_inception)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::Public;
|
use crate::{Public, PublicBox};
|
||||||
use rosenpass_util::{
|
use rosenpass_util::{
|
||||||
b64::b64_encode,
|
b64::b64_encode,
|
||||||
file::{
|
file::{
|
||||||
@@ -185,32 +327,35 @@ mod tests {
|
|||||||
Visibility,
|
Visibility,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{fs, os::unix::fs::PermissionsExt};
|
use std::{fs, ops::Deref, os::unix::fs::PermissionsExt};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
/// test loading a public from an example file, and then storing it again in a different file
|
/// Number of bytes in payload for load and store tests
|
||||||
#[test]
|
const N: usize = 100;
|
||||||
fn test_public_load_store() {
|
|
||||||
const N: usize = 100;
|
|
||||||
|
|
||||||
|
/// Convenience function for running a load/store test
|
||||||
|
fn run_load_store_test<
|
||||||
|
T: LoadValue<Error = anyhow::Error>
|
||||||
|
+ StoreValue<Error = anyhow::Error>
|
||||||
|
+ Deref<Target = [u8; N]>,
|
||||||
|
>() {
|
||||||
// Generate original random bytes
|
// Generate original random bytes
|
||||||
let original_bytes: [u8; N] = [rand::random(); N];
|
let original_bytes: [u8; N] = [rand::random(); N];
|
||||||
|
|
||||||
// Create a temporary directory
|
// Create a temporary directory
|
||||||
let temp_dir = tempdir().unwrap();
|
let temp_dir = tempdir().unwrap();
|
||||||
|
|
||||||
// Store the original public to an example file in the temporary directory
|
// Store the original bytes to an example file in the temporary directory
|
||||||
let example_file = temp_dir.path().join("example_file");
|
let example_file = temp_dir.path().join("example_file");
|
||||||
std::fs::write(example_file.clone(), &original_bytes).unwrap();
|
std::fs::write(&example_file, original_bytes).unwrap();
|
||||||
|
|
||||||
// Load the public from the example file
|
// Load the value from the example file into our generic type
|
||||||
|
let loaded_public = T::load(&example_file).unwrap();
|
||||||
|
|
||||||
let loaded_public = Public::load(&example_file).unwrap();
|
// Check that the loaded value matches the original bytes
|
||||||
|
assert_eq!(loaded_public.deref(), &original_bytes);
|
||||||
|
|
||||||
// Check that the loaded public matches the original bytes
|
// Store the loaded value to a different file in the temporary directory
|
||||||
assert_eq!(&loaded_public.value, &original_bytes);
|
|
||||||
|
|
||||||
// Store the loaded public to a different file in the temporary directory
|
|
||||||
let new_file = temp_dir.path().join("new_file");
|
let new_file = temp_dir.path().join("new_file");
|
||||||
loaded_public.store(&new_file).unwrap();
|
loaded_public.store(&new_file).unwrap();
|
||||||
|
|
||||||
@@ -224,10 +369,13 @@ mod tests {
|
|||||||
assert_eq!(new_file_contents, original_file_contents);
|
assert_eq!(new_file_contents, original_file_contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// test loading a base64 encoded public from an example file, and then storing it again in a different file
|
/// Convenience function for running a base64 load/store test
|
||||||
#[test]
|
fn run_base64_load_store_test<
|
||||||
fn test_public_load_store_base64() {
|
T: LoadValueB64<Error = anyhow::Error>
|
||||||
const N: usize = 100;
|
+ StoreValueB64<Error = anyhow::Error>
|
||||||
|
+ StoreValueB64Writer<Error = anyhow::Error>
|
||||||
|
+ Deref<Target = [u8; N]>,
|
||||||
|
>() {
|
||||||
// Generate original random bytes
|
// Generate original random bytes
|
||||||
let original_bytes: [u8; N] = [rand::random(); N];
|
let original_bytes: [u8; N] = [rand::random(); N];
|
||||||
// Create a temporary directory
|
// Create a temporary directory
|
||||||
@@ -238,9 +386,9 @@ mod tests {
|
|||||||
std::fs::write(&example_file, encoded_public).unwrap();
|
std::fs::write(&example_file, encoded_public).unwrap();
|
||||||
|
|
||||||
// Load the public from the example file
|
// Load the public from the example file
|
||||||
let loaded_public = Public::load_b64::<{ N * 2 }, _>(&example_file).unwrap();
|
let loaded_public = T::load_b64::<{ N * 2 }, _>(&example_file).unwrap();
|
||||||
// Check that the loaded public matches the original bytes
|
// Check that the loaded public matches the original bytes
|
||||||
assert_eq!(&loaded_public.value, &original_bytes);
|
assert_eq!(loaded_public.deref(), &original_bytes);
|
||||||
|
|
||||||
// Store the loaded public to a different file in the temporary directory
|
// Store the loaded public to a different file in the temporary directory
|
||||||
let new_file = temp_dir.path().join("new_file");
|
let new_file = temp_dir.path().join("new_file");
|
||||||
@@ -253,7 +401,7 @@ mod tests {
|
|||||||
// Check that the contents of the new file match the original file
|
// Check that the contents of the new file match the original file
|
||||||
assert_eq!(new_file_contents, original_file_contents);
|
assert_eq!(new_file_contents, original_file_contents);
|
||||||
|
|
||||||
//Check new file permissions are public
|
// Check new file permissions are public
|
||||||
let metadata = fs::metadata(&new_file).unwrap();
|
let metadata = fs::metadata(&new_file).unwrap();
|
||||||
assert_eq!(metadata.permissions().mode() & 0o000777, 0o644);
|
assert_eq!(metadata.permissions().mode() & 0o000777, 0o644);
|
||||||
|
|
||||||
@@ -271,9 +419,35 @@ mod tests {
|
|||||||
// Check that the contents of the new file match the original file
|
// Check that the contents of the new file match the original file
|
||||||
assert_eq!(new_file_contents, original_file_contents);
|
assert_eq!(new_file_contents, original_file_contents);
|
||||||
|
|
||||||
//Check new file permissions are public
|
// Check new file permissions are public
|
||||||
let metadata = fs::metadata(&new_file).unwrap();
|
let metadata = fs::metadata(&new_file).unwrap();
|
||||||
assert_eq!(metadata.permissions().mode() & 0o000777, 0o644);
|
assert_eq!(metadata.permissions().mode() & 0o000777, 0o644);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test loading a [Public] from an example file, and then storing it again in a new file
|
||||||
|
#[test]
|
||||||
|
fn test_public_load_store() {
|
||||||
|
run_load_store_test::<Public<N>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test loading a [PublicBox] from an example file, and then storing it again in a new file
|
||||||
|
#[test]
|
||||||
|
fn test_public_box_load_store() {
|
||||||
|
run_load_store_test::<PublicBox<N>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test loading a base64-encoded [Public] from an example file, and then storing it again
|
||||||
|
/// in a different file
|
||||||
|
#[test]
|
||||||
|
fn test_public_load_store_base64() {
|
||||||
|
run_base64_load_store_test::<Public<N>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test loading a base64-encoded [PublicBox] from an example file, and then storing it
|
||||||
|
/// again in a different file
|
||||||
|
#[test]
|
||||||
|
fn test_public_box_load_store_base64() {
|
||||||
|
run_base64_load_store_test::<PublicBox<N>>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -387,7 +386,7 @@ mod test {
|
|||||||
|
|
||||||
// Store the original secret to an example file in the temporary directory
|
// Store the original secret to an example file in the temporary directory
|
||||||
let example_file = temp_dir.path().join("example_file");
|
let example_file = temp_dir.path().join("example_file");
|
||||||
std::fs::write(example_file.clone(), &original_bytes).unwrap();
|
std::fs::write(&example_file, original_bytes).unwrap();
|
||||||
|
|
||||||
// Load the secret from the example file
|
// Load the secret from the example file
|
||||||
let loaded_secret = Secret::load(&example_file).unwrap();
|
let loaded_secret = Secret::load(&example_file).unwrap();
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
//! - `Dst: ?Sized`; (e.g. [u8]) – The target to write to
|
//! - `Dst: ?Sized`; (e.g. [u8]) – The target to write to
|
||||||
//! - `Out: Sized = &mut Dst`; (e.g. &mut [u8]) – A reference to the target to write to
|
//! - `Out: Sized = &mut Dst`; (e.g. &mut [u8]) – A reference to the target to write to
|
||||||
//! - `Coercable: ?Sized + DstCoercion<Dst>`; (e.g. `[u8]`, `[u8; 16]`) – Some value that
|
//! - `Coercable: ?Sized + DstCoercion<Dst>`; (e.g. `[u8]`, `[u8; 16]`) – Some value that
|
||||||
//! destination coercion can be applied to. Usually either `Dst` itself (e.g. `[u8]` or some sized variant of
|
//! destination coercion can be applied to. Usually either `Dst` itself (e.g. `[u8]` or some sized variant of
|
||||||
//! `Dst` (e.g. `[u8; 64]`).
|
//! `Dst` (e.g. `[u8; 64]`).
|
||||||
//! - `Ret: Sized`; (anything) – must be `CondenseBeside<_>` if condensing is to be applied. The ordinary return value of a function with an output
|
//! - `Ret: Sized`; (anything) – must be `CondenseBeside<_>` if condensing is to be applied. The ordinary return value of a function with an output
|
||||||
//! - `Val: Sized + BorrowMut<Dst>`; (e.g. [u8; 16]) – Some owned storage that can be borrowed as `Dst`
|
//! - `Val: Sized + BorrowMut<Dst>`; (e.g. [u8; 16]) – Some owned storage that can be borrowed as `Dst`
|
||||||
//! - `Condensed: Sized = CondenseBeside<Val>::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>) – The combiation of Val and Ret after condensing was applied (`Beside<Val, Ret>::condense()`/`Ret::condense(v)` for all `v : Val`).
|
//! - `Condensed: Sized = CondenseBeside<Val>::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>) – The combiation of Val and Ret after condensing was applied (`Beside<Val, Ret>::condense()`/`Ret::condense(v)` for all `v : Val`).
|
||||||
|
|||||||
@@ -18,3 +18,6 @@ typenum = { workspace = true }
|
|||||||
static_assertions = { workspace = true }
|
static_assertions = { workspace = true }
|
||||||
rustix = {workspace = true}
|
rustix = {workspace = true}
|
||||||
zeroize = {workspace = true}
|
zeroize = {workspace = true}
|
||||||
|
zerocopy = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
mio = { workspace = true }
|
||||||
|
|||||||
@@ -1,12 +1,50 @@
|
|||||||
use std::os::fd::{OwnedFd, RawFd};
|
use rustix::{
|
||||||
|
fd::{AsFd, BorrowedFd, FromRawFd, OwnedFd, RawFd},
|
||||||
|
io::{fcntl_dupfd_cloexec, DupFlags},
|
||||||
|
};
|
||||||
|
|
||||||
/// Clone some file descriptor
|
use crate::mem::Forgetting;
|
||||||
|
|
||||||
|
/// Prepare a file descriptor for use in Rust code.
|
||||||
///
|
///
|
||||||
/// If the file descriptor is invalid, an error will be raised.
|
/// Checks if the file descriptor is valid and duplicates it to a new file descriptor.
|
||||||
pub fn claim_fd(fd: RawFd) -> anyhow::Result<OwnedFd> {
|
/// The old file descriptor is masked to avoid potential use after free (on file descriptor)
|
||||||
use rustix::{fd::BorrowedFd, io::dup};
|
/// in case the given file descriptor is still used somewhere
|
||||||
|
pub fn claim_fd(fd: RawFd) -> rustix::io::Result<OwnedFd> {
|
||||||
// This is safe since [dup] will simply raise
|
let new = clone_fd_cloexec(unsafe { BorrowedFd::borrow_raw(fd) })?;
|
||||||
let fd = unsafe { dup(BorrowedFd::borrow_raw(fd))? };
|
mask_fd(fd)?;
|
||||||
Ok(fd)
|
Ok(new)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mask_fd(fd: RawFd) -> rustix::io::Result<()> {
|
||||||
|
// Safety: because the OwnedFd resulting from OwnedFd::from_raw_fd is wrapped in a Forgetting,
|
||||||
|
// it never gets dropped, meaning that fd is never closed and thus outlives the OwnedFd
|
||||||
|
let mut owned = Forgetting::new(unsafe { OwnedFd::from_raw_fd(fd) });
|
||||||
|
clone_fd_to_cloexec(open_nullfd()?, &mut owned)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone_fd_cloexec<Fd: AsFd>(fd: Fd) -> rustix::io::Result<OwnedFd> {
|
||||||
|
const MINFD: RawFd = 3; // Avoid stdin, stdout, and stderr
|
||||||
|
fcntl_dupfd_cloexec(fd, MINFD)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn clone_fd_to_cloexec<Fd: AsFd>(fd: Fd, new: &mut OwnedFd) -> rustix::io::Result<()> {
|
||||||
|
use rustix::io::dup3;
|
||||||
|
dup3(fd, new, DupFlags::CLOEXEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn clone_fd_to_cloexec<Fd: AsFd>(fd: Fd, new: &mut OwnedFd) -> rustix::io::Result<()> {
|
||||||
|
use rustix::io::{dup2, fcntl_setfd, FdFlags};
|
||||||
|
dup2(&fd, new)?;
|
||||||
|
fcntl_setfd(&new, FdFlags::CLOEXEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open a "blocked" file descriptor. I.e. a file descriptor that is neither meant for reading nor
|
||||||
|
/// writing
|
||||||
|
pub fn open_nullfd() -> rustix::io::Result<OwnedFd> {
|
||||||
|
use rustix::fs::{open, Mode, OFlags};
|
||||||
|
// TODO: Add tests showing that this will throw errors on use
|
||||||
|
open("/dev/null", OFlags::CLOEXEC, Mode::empty())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use anyhow::ensure;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
use std::result::Result;
|
|
||||||
use std::{fs::OpenOptions, path::Path};
|
use std::{fs::OpenOptions, path::Path};
|
||||||
|
|
||||||
pub enum Visibility {
|
pub enum Visibility {
|
||||||
|
|||||||
@@ -13,3 +13,7 @@ where
|
|||||||
f(&v);
|
f(&v);
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run<R, F: FnOnce() -> R>(f: F) -> R {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|||||||
110
util/src/io.rs
Normal file
110
util/src/io.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
use std::{borrow::Borrow, io};
|
||||||
|
|
||||||
|
pub trait IoErrorKind {
|
||||||
|
fn io_error_kind(&self) -> io::ErrorKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Borrow<io::Error>> IoErrorKind for T {
|
||||||
|
fn io_error_kind(&self) -> io::ErrorKind {
|
||||||
|
self.borrow().kind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TryIoErrorKind {
|
||||||
|
fn try_io_error_kind(&self) -> Option<io::ErrorKind>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: IoErrorKind> TryIoErrorKind for T {
|
||||||
|
fn try_io_error_kind(&self) -> Option<io::ErrorKind> {
|
||||||
|
Some(self.io_error_kind())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IoResultKindHintExt<T>: Sized {
|
||||||
|
type Error;
|
||||||
|
fn io_err_kind_hint(self) -> Result<T, (Self::Error, io::ErrorKind)>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E: IoErrorKind> IoResultKindHintExt<T> for Result<T, E> {
|
||||||
|
type Error = E;
|
||||||
|
fn io_err_kind_hint(self) -> Result<T, (E, io::ErrorKind)> {
|
||||||
|
self.map_err(|e| {
|
||||||
|
let kind = e.borrow().io_error_kind();
|
||||||
|
(e, kind)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TryIoResultKindHintExt<T>: Sized {
|
||||||
|
type Error;
|
||||||
|
fn try_io_err_kind_hint(self) -> Result<T, (Self::Error, Option<io::ErrorKind>)>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E: TryIoErrorKind> TryIoResultKindHintExt<T> for Result<T, E> {
|
||||||
|
type Error = E;
|
||||||
|
fn try_io_err_kind_hint(self) -> Result<T, (E, Option<io::ErrorKind>)> {
|
||||||
|
self.map_err(|e| {
|
||||||
|
let opt_kind = e.borrow().try_io_error_kind();
|
||||||
|
(e, opt_kind)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Automatically handles `std::io::ErrorKind::Interrupted`.
|
||||||
|
///
|
||||||
|
/// - If there is no error (i.e. on `Ok(r)`), the function will return `Ok(Some(r))`
|
||||||
|
/// - `Interrupted` is handled internally, by retrying the IO operation
|
||||||
|
/// - Other errors are returned as is
|
||||||
|
pub fn handle_interrupted<R, E, F>(mut iofn: F) -> Result<Option<R>, E>
|
||||||
|
where
|
||||||
|
E: TryIoErrorKind,
|
||||||
|
F: FnMut() -> Result<R, E>,
|
||||||
|
{
|
||||||
|
use io::ErrorKind as E;
|
||||||
|
loop {
|
||||||
|
match iofn().try_io_err_kind_hint() {
|
||||||
|
Ok(v) => return Ok(Some(v)),
|
||||||
|
Err((_, Some(E::Interrupted))) => continue, // try again
|
||||||
|
Err((e, _)) => return Err(e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Automatically handles `std::io::ErrorKind::{WouldBlock, Interrupted}`.
|
||||||
|
///
|
||||||
|
/// - If there is no error (i.e. on `Ok(r)`), the function will return `Ok(Some(r))`
|
||||||
|
/// - `Interrupted` is handled internally, by retrying the IO operation
|
||||||
|
/// - `WouldBlock` is handled by returning `Ok(None)`,
|
||||||
|
/// - Other errors are returned as is
|
||||||
|
pub fn nonblocking_handle_io_errors<R, E, F>(mut iofn: F) -> Result<Option<R>, E>
|
||||||
|
where
|
||||||
|
E: TryIoErrorKind,
|
||||||
|
F: FnMut() -> Result<R, E>,
|
||||||
|
{
|
||||||
|
use io::ErrorKind as E;
|
||||||
|
loop {
|
||||||
|
match iofn().try_io_err_kind_hint() {
|
||||||
|
Ok(v) => return Ok(Some(v)),
|
||||||
|
Err((_, Some(E::WouldBlock))) => return Ok(None), // no data to read
|
||||||
|
Err((_, Some(E::Interrupted))) => continue, // try again
|
||||||
|
Err((e, _)) => return Err(e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ReadNonblockingWithBoringErrorsHandledExt {
|
||||||
|
/// Convenience wrapper using [nonblocking_handle_io_errors] with [std::io::Read]
|
||||||
|
fn read_nonblocking_with_boring_errors_handled(
|
||||||
|
&mut self,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> io::Result<Option<usize>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: io::Read> ReadNonblockingWithBoringErrorsHandledExt for T {
|
||||||
|
fn read_nonblocking_with_boring_errors_handled(
|
||||||
|
&mut self,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> io::Result<Option<usize>> {
|
||||||
|
nonblocking_handle_io_errors(|| self.read(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
359
util/src/length_prefix_encoding/decoder.rs
Normal file
359
util/src/length_prefix_encoding/decoder.rs
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
use std::{borrow::BorrowMut, cmp::min, io};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
io::{TryIoErrorKind, TryIoResultKindHintExt},
|
||||||
|
result::ensure_or,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const HEADER_SIZE: usize = std::mem::size_of::<u64>();
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum SanityError {
|
||||||
|
#[error("Offset is out of read buffer bounds")]
|
||||||
|
OutOfBufferBounds,
|
||||||
|
#[error("Offset is out of message buffer bounds")]
|
||||||
|
OutOfMessageBounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("Message too large ({msg_size} bytes) for buffer ({buf_size} bytes)")]
|
||||||
|
pub struct MessageTooLargeError {
|
||||||
|
msg_size: usize,
|
||||||
|
buf_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageTooLargeError {
|
||||||
|
pub fn new(msg_size: usize, buf_size: usize) -> Self {
|
||||||
|
Self { msg_size, buf_size }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure(msg_size: usize, buf_size: usize) -> Result<(), Self> {
|
||||||
|
let err = MessageTooLargeError { msg_size, buf_size };
|
||||||
|
ensure_or(msg_size <= buf_size, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ReadFromIoReturn<'a> {
|
||||||
|
pub bytes_read: usize,
|
||||||
|
pub message: Option<&'a mut [u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ReadFromIoReturn<'a> {
|
||||||
|
pub fn new(bytes_read: usize, message: Option<&'a mut [u8]>) -> Self {
|
||||||
|
Self {
|
||||||
|
bytes_read,
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ReadFromIoError {
|
||||||
|
#[error("Error reading from the underlying stream")]
|
||||||
|
IoError(#[from] io::Error),
|
||||||
|
#[error("Message size out of buffer bounds")]
|
||||||
|
MessageTooLargeError(#[from] MessageTooLargeError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryIoErrorKind for ReadFromIoError {
|
||||||
|
fn try_io_error_kind(&self) -> Option<io::ErrorKind> {
|
||||||
|
match self {
|
||||||
|
ReadFromIoError::IoError(ioe) => Some(ioe.kind()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct LengthPrefixDecoder<Buf: BorrowMut<[u8]>> {
|
||||||
|
header: [u8; HEADER_SIZE],
|
||||||
|
buf: Buf,
|
||||||
|
off: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||||
|
pub fn new(buf: Buf) -> Self {
|
||||||
|
let header = Default::default();
|
||||||
|
let off = 0;
|
||||||
|
Self { header, buf, off }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.zeroize()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_parts(header: [u8; HEADER_SIZE], buf: Buf, off: usize) -> Self {
|
||||||
|
Self { header, buf, off }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_parts(self) -> ([u8; HEADER_SIZE], Buf, usize) {
|
||||||
|
let Self { header, buf, off } = self;
|
||||||
|
(header, buf, off)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_all_from_stdio<R: io::Read>(
|
||||||
|
&mut self,
|
||||||
|
mut r: R,
|
||||||
|
) -> Result<&mut [u8], ReadFromIoError> {
|
||||||
|
use io::ErrorKind as K;
|
||||||
|
loop {
|
||||||
|
match self.read_from_stdio(&mut r).try_io_err_kind_hint() {
|
||||||
|
// Success (appeasing the borrow checker by calling message_mut())
|
||||||
|
Ok(ReadFromIoReturn {
|
||||||
|
message: Some(_), ..
|
||||||
|
}) => break Ok(self.message_mut().unwrap().unwrap()),
|
||||||
|
|
||||||
|
// Unexpected EOF
|
||||||
|
Ok(ReadFromIoReturn { bytes_read: 0, .. }) => {
|
||||||
|
break Err(ReadFromIoError::IoError(io::Error::new(
|
||||||
|
K::UnexpectedEof,
|
||||||
|
"",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry
|
||||||
|
Ok(ReadFromIoReturn { message: None, .. }) => continue,
|
||||||
|
Err((_, Some(K::Interrupted))) => continue,
|
||||||
|
|
||||||
|
// Other error
|
||||||
|
Err((e, _)) => break Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_from_stdio<R: io::Read>(
|
||||||
|
&mut self,
|
||||||
|
mut r: R,
|
||||||
|
) -> Result<ReadFromIoReturn, ReadFromIoError> {
|
||||||
|
Ok(match self.next_slice_to_write_to()? {
|
||||||
|
// Read some bytes; any MessageTooLargeError in the call to self.message_mut() is
|
||||||
|
// ignored to ensure this function changes no state upon errors; the user should rerun
|
||||||
|
// the function and collect the MessageTooLargeError on the following invocation
|
||||||
|
Some(buf) => {
|
||||||
|
let bytes_read = r.read(buf)?;
|
||||||
|
self.advance(bytes_read).unwrap();
|
||||||
|
let message = self.message_mut().ok().flatten();
|
||||||
|
ReadFromIoReturn {
|
||||||
|
bytes_read,
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Message is already fully read; full delegation to self.message_mut()
|
||||||
|
None => ReadFromIoReturn {
|
||||||
|
bytes_read: 0,
|
||||||
|
message: self.message_mut()?,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_slice_to_write_to(&mut self) -> Result<Option<&mut [u8]>, MessageTooLargeError> {
|
||||||
|
fn some_if_nonempty(buf: &mut [u8]) -> Option<&mut [u8]> {
|
||||||
|
match buf.is_empty() {
|
||||||
|
true => None,
|
||||||
|
false => Some(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! return_if_nonempty_some {
|
||||||
|
($opt:expr) => {{
|
||||||
|
// Deliberate double expansion of $opt to appease the borrow checker *sigh*
|
||||||
|
if $opt.and_then(some_if_nonempty).is_some() {
|
||||||
|
return Ok($opt);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
return_if_nonempty_some!(Some(self.header_buffer_left_mut()));
|
||||||
|
return_if_nonempty_some!(self.message_fragment_left_mut()?);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advance(&mut self, count: usize) -> Result<(), SanityError> {
|
||||||
|
let off = self.off + count;
|
||||||
|
let msg_off = off.saturating_sub(HEADER_SIZE);
|
||||||
|
|
||||||
|
use SanityError as E;
|
||||||
|
let alloc = self.message_buffer().len();
|
||||||
|
let msgsz = self.message_size();
|
||||||
|
ensure_or(msg_off <= alloc, E::OutOfBufferBounds)?;
|
||||||
|
ensure_or(
|
||||||
|
msgsz.map(|s| msg_off <= s).unwrap_or(true),
|
||||||
|
E::OutOfMessageBounds,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.off = off;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_sufficient_msg_buffer(&self) -> Result<(), MessageTooLargeError> {
|
||||||
|
let buf_size = self.message_buffer().len();
|
||||||
|
let msg_size = match self.get_header() {
|
||||||
|
None => return Ok(()),
|
||||||
|
Some(v) => v,
|
||||||
|
};
|
||||||
|
MessageTooLargeError::ensure(msg_size, buf_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_buffer(&self) -> &[u8] {
|
||||||
|
&self.header[..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_buffer_mut(&mut self) -> &mut [u8] {
|
||||||
|
&mut self.header[..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_buffer(&self) -> &[u8] {
|
||||||
|
self.buf.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_buffer_mut(&mut self) -> &mut [u8] {
|
||||||
|
self.buf.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_read(&self) -> &usize {
|
||||||
|
&self.off
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_message_buffer(self) -> Buf {
|
||||||
|
let Self { buf, .. } = self;
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_buffer_offset(&self) -> usize {
|
||||||
|
min(self.off, HEADER_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_buffer_offset(&self) -> usize {
|
||||||
|
self.off.saturating_sub(HEADER_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_header(&self) -> bool {
|
||||||
|
self.header_buffer_offset() == HEADER_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_message(&self) -> Result<bool, MessageTooLargeError> {
|
||||||
|
self.ensure_sufficient_msg_buffer()?;
|
||||||
|
let msg_size = match self.get_header() {
|
||||||
|
None => return Ok(false),
|
||||||
|
Some(v) => v,
|
||||||
|
};
|
||||||
|
Ok(self.message_buffer_avail().len() == msg_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_buffer_avail(&self) -> &[u8] {
|
||||||
|
let off = self.header_buffer_offset();
|
||||||
|
&self.header_buffer()[..off]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_buffer_avail_mut(&mut self) -> &mut [u8] {
|
||||||
|
let off = self.header_buffer_offset();
|
||||||
|
&mut self.header_buffer_mut()[..off]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_buffer_left(&self) -> &[u8] {
|
||||||
|
let off = self.header_buffer_offset();
|
||||||
|
&self.header_buffer()[off..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_buffer_left_mut(&mut self) -> &mut [u8] {
|
||||||
|
let off = self.header_buffer_offset();
|
||||||
|
&mut self.header_buffer_mut()[off..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_buffer_avail(&self) -> &[u8] {
|
||||||
|
let off = self.message_buffer_offset();
|
||||||
|
&self.message_buffer()[..off]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_buffer_avail_mut(&mut self) -> &mut [u8] {
|
||||||
|
let off = self.message_buffer_offset();
|
||||||
|
&mut self.message_buffer_mut()[..off]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_buffer_left(&self) -> &[u8] {
|
||||||
|
let off = self.message_buffer_offset();
|
||||||
|
&self.message_buffer()[off..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_buffer_left_mut(&mut self) -> &mut [u8] {
|
||||||
|
let off = self.message_buffer_offset();
|
||||||
|
&mut self.message_buffer_mut()[off..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_header(&self) -> Option<usize> {
|
||||||
|
match self.header_buffer_offset() == HEADER_SIZE {
|
||||||
|
false => None,
|
||||||
|
true => Some(u64::from_le_bytes(self.header) as usize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_size(&self) -> Option<usize> {
|
||||||
|
self.get_header()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encoded_message_bytes(&self) -> Option<usize> {
|
||||||
|
self.message_size().map(|sz| sz + HEADER_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_fragment(&self) -> Result<Option<&[u8]>, MessageTooLargeError> {
|
||||||
|
self.ensure_sufficient_msg_buffer()?;
|
||||||
|
Ok(self.message_size().map(|sz| &self.message_buffer()[..sz]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_fragment_mut(&mut self) -> Result<Option<&mut [u8]>, MessageTooLargeError> {
|
||||||
|
self.ensure_sufficient_msg_buffer()?;
|
||||||
|
Ok(self
|
||||||
|
.message_size()
|
||||||
|
.map(|sz| &mut self.message_buffer_mut()[..sz]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_fragment_avail(&self) -> Result<Option<&[u8]>, MessageTooLargeError> {
|
||||||
|
let off = self.message_buffer_avail().len();
|
||||||
|
self.message_fragment()
|
||||||
|
.map(|frag| frag.map(|frag| &frag[..off]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_fragment_avail_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Option<&mut [u8]>, MessageTooLargeError> {
|
||||||
|
let off = self.message_buffer_avail().len();
|
||||||
|
self.message_fragment_mut()
|
||||||
|
.map(|frag| frag.map(|frag| &mut frag[..off]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_fragment_left(&self) -> Result<Option<&[u8]>, MessageTooLargeError> {
|
||||||
|
let off = self.message_buffer_avail().len();
|
||||||
|
self.message_fragment()
|
||||||
|
.map(|frag| frag.map(|frag| &frag[off..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_fragment_left_mut(&mut self) -> Result<Option<&mut [u8]>, MessageTooLargeError> {
|
||||||
|
let off = self.message_buffer_avail().len();
|
||||||
|
self.message_fragment_mut()
|
||||||
|
.map(|frag| frag.map(|frag| &mut frag[off..]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&self) -> Result<Option<&[u8]>, MessageTooLargeError> {
|
||||||
|
let sz = self.message_size();
|
||||||
|
self.message_fragment_avail()
|
||||||
|
.map(|frag_opt| frag_opt.and_then(|frag| (frag.len() == sz?).then_some(frag)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_mut(&mut self) -> Result<Option<&mut [u8]>, MessageTooLargeError> {
|
||||||
|
let sz = self.message_size();
|
||||||
|
self.message_fragment_avail_mut()
|
||||||
|
.map(|frag_opt| frag_opt.and_then(|frag| (frag.len() == sz?).then_some(frag)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Buf: BorrowMut<[u8]>> Zeroize for LengthPrefixDecoder<Buf> {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.header.zeroize();
|
||||||
|
self.message_buffer_mut().zeroize();
|
||||||
|
self.off.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
381
util/src/length_prefix_encoding/encoder.rs
Normal file
381
util/src/length_prefix_encoding/encoder.rs
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::{Borrow, BorrowMut},
|
||||||
|
cmp::min,
|
||||||
|
io,
|
||||||
|
};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use crate::{io::IoResultKindHintExt, result::ensure_or};
|
||||||
|
|
||||||
|
pub const HEADER_SIZE: usize = std::mem::size_of::<u64>();
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
|
#[error("Write position is out of buffer bounds")]
|
||||||
|
pub struct PositionOutOfBufferBounds;
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
|
#[error("Write position is out of message bounds")]
|
||||||
|
pub struct PositionOutOfMessageBounds;
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
|
#[error("Write position is out of header bounds")]
|
||||||
|
pub struct PositionOutOfHeaderBounds;
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
|
#[error("Message length is bigger than buffer length")]
|
||||||
|
pub struct MessageTooLarge;
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
|
pub enum MessageLenSanityError {
|
||||||
|
#[error("{0:?}")]
|
||||||
|
PositionOutOfMessageBounds(#[from] PositionOutOfMessageBounds),
|
||||||
|
#[error("{0:?}")]
|
||||||
|
MessageTooLarge(#[from] MessageTooLarge),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
|
pub enum PositionSanityError {
|
||||||
|
#[error("{0:?}")]
|
||||||
|
PositionOutOfMessageBounds(#[from] PositionOutOfMessageBounds),
|
||||||
|
#[error("{0:?}")]
|
||||||
|
PositionOutOfBufferBounds(#[from] PositionOutOfBufferBounds),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug, Clone, Copy)]
|
||||||
|
pub enum SanityError {
|
||||||
|
#[error("{0:?}")]
|
||||||
|
PositionOutOfMessageBounds(#[from] PositionOutOfMessageBounds),
|
||||||
|
#[error("{0:?}")]
|
||||||
|
PositionOutOfBufferBounds(#[from] PositionOutOfBufferBounds),
|
||||||
|
#[error("{0:?}")]
|
||||||
|
MessageTooLarge(#[from] MessageTooLarge),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SanityError> for MessageLenSanityError {
|
||||||
|
type Error = PositionOutOfBufferBounds;
|
||||||
|
|
||||||
|
fn try_from(value: SanityError) -> Result<Self, Self::Error> {
|
||||||
|
use {MessageLenSanityError as T, SanityError as F};
|
||||||
|
match value {
|
||||||
|
F::PositionOutOfMessageBounds(e) => Ok(T::PositionOutOfMessageBounds(e)),
|
||||||
|
F::MessageTooLarge(e) => Ok(T::MessageTooLarge(e)),
|
||||||
|
F::PositionOutOfBufferBounds(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MessageLenSanityError> for SanityError {
|
||||||
|
fn from(value: MessageLenSanityError) -> Self {
|
||||||
|
use {MessageLenSanityError as F, SanityError as T};
|
||||||
|
match value {
|
||||||
|
F::PositionOutOfMessageBounds(e) => T::PositionOutOfMessageBounds(e),
|
||||||
|
F::MessageTooLarge(e) => T::MessageTooLarge(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PositionSanityError> for SanityError {
|
||||||
|
fn from(value: PositionSanityError) -> Self {
|
||||||
|
use {PositionSanityError as F, SanityError as T};
|
||||||
|
match value {
|
||||||
|
F::PositionOutOfBufferBounds(e) => T::PositionOutOfBufferBounds(e),
|
||||||
|
F::PositionOutOfMessageBounds(e) => T::PositionOutOfMessageBounds(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WriteToIoReturn {
|
||||||
|
pub bytes_written: usize,
|
||||||
|
pub done: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct LengthPrefixEncoder<Buf: Borrow<[u8]>> {
|
||||||
|
buf: Buf,
|
||||||
|
header: [u8; HEADER_SIZE],
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||||
|
pub fn from_buffer(buf: Buf) -> Self {
|
||||||
|
let (header, pos) = ([0u8; HEADER_SIZE], 0);
|
||||||
|
let mut r = Self { buf, header, pos };
|
||||||
|
r.clear();
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_message(msg: Buf) -> Self {
|
||||||
|
let mut r = Self::from_buffer(msg);
|
||||||
|
r.restart_write_with_new_message(r.buffer_bytes().len())
|
||||||
|
.unwrap();
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_short_message(msg: Buf, len: usize) -> Result<Self, MessageLenSanityError> {
|
||||||
|
let mut r = Self::from_message(msg);
|
||||||
|
r.set_message_len(len)?;
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_parts(buf: Buf, len: usize, pos: usize) -> Result<Self, SanityError> {
|
||||||
|
let mut r = Self::from_buffer(buf);
|
||||||
|
r.set_msg_len_and_position(len, pos)?;
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_buffer(self) -> Buf {
|
||||||
|
let Self { buf, .. } = self;
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_parts(self) -> (Buf, usize, usize) {
|
||||||
|
let len = self.message_len();
|
||||||
|
let pos = self.writing_position();
|
||||||
|
let buf = self.into_buffer();
|
||||||
|
(buf, len, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.set_msg_len_and_position(0, 0).unwrap();
|
||||||
|
self.set_message_offset(0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_all_to_stdio<W: io::Write>(&mut self, mut w: W) -> io::Result<()> {
|
||||||
|
use io::ErrorKind as K;
|
||||||
|
loop {
|
||||||
|
match self.write_to_stdio(&mut w).io_err_kind_hint() {
|
||||||
|
// Done
|
||||||
|
Ok(WriteToIoReturn { done: true, .. }) => break Ok(()),
|
||||||
|
|
||||||
|
// Retry
|
||||||
|
Ok(WriteToIoReturn { done: false, .. }) => continue,
|
||||||
|
Err((_, K::Interrupted)) => continue,
|
||||||
|
|
||||||
|
Err((e, _)) => break Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to_stdio<W: io::Write>(&mut self, mut w: W) -> io::Result<WriteToIoReturn> {
|
||||||
|
if self.exhausted() {
|
||||||
|
return Ok(WriteToIoReturn {
|
||||||
|
bytes_written: 0,
|
||||||
|
done: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = self.next_slice_to_write();
|
||||||
|
let bytes_written = w.write(buf)?;
|
||||||
|
self.advance(bytes_written).unwrap();
|
||||||
|
|
||||||
|
let done = self.exhausted();
|
||||||
|
Ok(WriteToIoReturn {
|
||||||
|
bytes_written,
|
||||||
|
done,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restart_write(&mut self) {
|
||||||
|
self.set_writing_position(0).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restart_write_with_new_message(
|
||||||
|
&mut self,
|
||||||
|
len: usize,
|
||||||
|
) -> Result<(), MessageLenSanityError> {
|
||||||
|
self.set_msg_len_and_position(len, 0)
|
||||||
|
.map_err(|e| e.try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_slice_to_write(&self) -> &[u8] {
|
||||||
|
let s = self.header_left();
|
||||||
|
if !s.is_empty() {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = self.message_left();
|
||||||
|
if !s.is_empty() {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exhausted(&self) -> bool {
|
||||||
|
self.next_slice_to_write().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&self) -> &[u8] {
|
||||||
|
&self.buffer_bytes()[..self.message_len()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_written(&self) -> &[u8] {
|
||||||
|
&self.header()[..self.header_offset()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_left(&self) -> &[u8] {
|
||||||
|
&self.header()[self.header_offset()..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_written(&self) -> &[u8] {
|
||||||
|
&self.message()[..self.message_offset()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_left(&self) -> &[u8] {
|
||||||
|
&self.message()[self.message_offset()..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buf(&self) -> &Buf {
|
||||||
|
&self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer_bytes(&self) -> &[u8] {
|
||||||
|
self.buf().borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_header(&self) -> u64 {
|
||||||
|
u64::from_le_bytes(self.header)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header(&self) -> &[u8; HEADER_SIZE] {
|
||||||
|
&self.header
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_len(&self) -> usize {
|
||||||
|
self.decode_header() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encoded_message_bytes(&self) -> usize {
|
||||||
|
self.message_len() + HEADER_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writing_position(&self) -> usize {
|
||||||
|
self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_offset(&self) -> usize {
|
||||||
|
min(self.writing_position(), HEADER_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_offset(&self) -> usize {
|
||||||
|
self.writing_position().saturating_sub(HEADER_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_header(&mut self, header: [u8; HEADER_SIZE]) -> Result<(), MessageLenSanityError> {
|
||||||
|
self.offset_transaction(|t| {
|
||||||
|
t.header = header;
|
||||||
|
t.ensure_msg_in_buf_bounds()?;
|
||||||
|
t.ensure_pos_in_msg_bounds()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_and_set_header(&mut self, header: u64) -> Result<(), MessageLenSanityError> {
|
||||||
|
self.set_header(header.to_le_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_message_len(&mut self, len: usize) -> Result<(), MessageLenSanityError> {
|
||||||
|
self.encode_and_set_header(len as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_writing_position(&mut self, pos: usize) -> Result<(), PositionSanityError> {
|
||||||
|
self.offset_transaction(|t| {
|
||||||
|
t.pos = pos;
|
||||||
|
t.ensure_pos_in_buf_bounds()?;
|
||||||
|
t.ensure_pos_in_msg_bounds()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_header_offset(&mut self, off: usize) -> Result<(), PositionOutOfHeaderBounds> {
|
||||||
|
ensure_or(off <= HEADER_SIZE, PositionOutOfHeaderBounds)?;
|
||||||
|
self.set_writing_position(off).unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_message_offset(&mut self, off: usize) -> Result<(), PositionSanityError> {
|
||||||
|
self.set_writing_position(off + HEADER_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advance(&mut self, off: usize) -> Result<(), PositionSanityError> {
|
||||||
|
self.set_writing_position(self.writing_position() + off)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_msg_len_and_position(&mut self, len: usize, pos: usize) -> Result<(), SanityError> {
|
||||||
|
self.pos = 0;
|
||||||
|
self.set_message_len(len)?;
|
||||||
|
self.set_writing_position(pos)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_transaction<E, F>(&mut self, f: F) -> Result<(), E>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut LengthPrefixEncoder<&[u8]>) -> Result<(), E>,
|
||||||
|
{
|
||||||
|
let (header, pos) = {
|
||||||
|
let (buf, header, pos) = (self.buffer_bytes(), self.header, self.pos);
|
||||||
|
let mut tmp = LengthPrefixEncoder { buf, header, pos };
|
||||||
|
f(&mut tmp)?;
|
||||||
|
Ok((tmp.header, tmp.pos))
|
||||||
|
}?;
|
||||||
|
(self.header, self.pos) = (header, pos);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_pos_in_buf_bounds(&self) -> Result<(), PositionOutOfBufferBounds> {
|
||||||
|
ensure_or(
|
||||||
|
self.message_offset() <= self.buffer_bytes().len(),
|
||||||
|
PositionOutOfBufferBounds,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_pos_in_msg_bounds(&self) -> Result<(), PositionOutOfMessageBounds> {
|
||||||
|
ensure_or(
|
||||||
|
self.message_offset() <= self.message_len(),
|
||||||
|
PositionOutOfMessageBounds,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_msg_in_buf_bounds(&self) -> Result<(), MessageTooLarge> {
|
||||||
|
ensure_or(
|
||||||
|
self.message_len() <= self.buffer_bytes().len(),
|
||||||
|
MessageTooLarge,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Buf: BorrowMut<[u8]>> LengthPrefixEncoder<Buf> {
|
||||||
|
pub fn buf_mut(&mut self) -> &mut Buf {
|
||||||
|
&mut self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer_bytes_mut(&mut self) -> &mut [u8] {
|
||||||
|
self.buf.borrow_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_mut(&mut self) -> &mut [u8] {
|
||||||
|
let off = self.message_len();
|
||||||
|
&mut self.buffer_bytes_mut()[..off]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_written_mut(&mut self) -> &mut [u8] {
|
||||||
|
let off = self.message_offset();
|
||||||
|
&mut self.message_mut()[..off]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_left_mut(&mut self) -> &mut [u8] {
|
||||||
|
let off = self.message_offset();
|
||||||
|
&mut self.message_mut()[off..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Buf: BorrowMut<[u8]>> Zeroize for LengthPrefixEncoder<Buf> {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.buffer_bytes_mut().zeroize();
|
||||||
|
self.header.zeroize();
|
||||||
|
self.pos.zeroize();
|
||||||
|
self.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
2
util/src/length_prefix_encoding/mod.rs
Normal file
2
util/src/length_prefix_encoding/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod decoder;
|
||||||
|
pub mod encoder;
|
||||||
@@ -4,8 +4,13 @@ pub mod b64;
|
|||||||
pub mod fd;
|
pub mod fd;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod functional;
|
pub mod functional;
|
||||||
|
pub mod io;
|
||||||
|
pub mod length_prefix_encoding;
|
||||||
pub mod mem;
|
pub mod mem;
|
||||||
|
pub mod mio;
|
||||||
pub mod ord;
|
pub mod ord;
|
||||||
pub mod result;
|
pub mod result;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod typenum;
|
pub mod typenum;
|
||||||
|
pub mod zerocopy;
|
||||||
|
pub mod zeroize;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use std::borrow::{Borrow, BorrowMut};
|
use std::borrow::{Borrow, BorrowMut};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
use std::mem::{forget, swap};
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
/// Concatenate two byte arrays
|
/// Concatenate two byte arrays
|
||||||
// TODO: Zeroize result?
|
// TODO: Zeroize result?
|
||||||
@@ -31,3 +33,62 @@ pub fn cpy_min<T: BorrowMut<[u8]> + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, d
|
|||||||
let len = min(src.len(), dst.len());
|
let len = min(src.len(), dst.len());
|
||||||
dst[..len].copy_from_slice(&src[..len]);
|
dst[..len].copy_from_slice(&src[..len]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrapper type to inhibit calling [std::mem::Drop] when the underlying variable is freed
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Default)]
|
||||||
|
pub struct Forgetting<T> {
|
||||||
|
value: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Forgetting<T> {
|
||||||
|
pub fn new(value: T) -> Self {
|
||||||
|
let value = Some(value);
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract(mut self) -> T {
|
||||||
|
let mut value = None;
|
||||||
|
swap(&mut value, &mut self.value);
|
||||||
|
value.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for Forgetting<T> {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Forgetting<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.value.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for Forgetting<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.value.as_mut().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Borrow<T> for Forgetting<T> {
|
||||||
|
fn borrow(&self) -> &T {
|
||||||
|
self.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BorrowMut<T> for Forgetting<T> {
|
||||||
|
fn borrow_mut(&mut self) -> &mut T {
|
||||||
|
self.deref_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for Forgetting<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut value = None;
|
||||||
|
swap(&mut self.value, &mut value);
|
||||||
|
forget(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
39
util/src/mio.rs
Normal file
39
util/src/mio.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use mio::net::{UnixListener, UnixStream};
|
||||||
|
use rustix::fd::RawFd;
|
||||||
|
|
||||||
|
use crate::fd::claim_fd;
|
||||||
|
|
||||||
|
pub mod interest {
|
||||||
|
use mio::Interest;
|
||||||
|
pub const R: Interest = Interest::READABLE;
|
||||||
|
pub const W: Interest = Interest::WRITABLE;
|
||||||
|
pub const RW: Interest = R.add(W);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UnixListenerExt: Sized {
|
||||||
|
fn claim_fd(fd: RawFd) -> anyhow::Result<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnixListenerExt for UnixListener {
|
||||||
|
fn claim_fd(fd: RawFd) -> anyhow::Result<Self> {
|
||||||
|
use std::os::unix::net::UnixListener as StdUnixListener;
|
||||||
|
|
||||||
|
let sock = StdUnixListener::from(claim_fd(fd)?);
|
||||||
|
sock.set_nonblocking(true)?;
|
||||||
|
Ok(UnixListener::from_std(sock))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UnixStreamExt: Sized {
|
||||||
|
fn claim_fd(fd: RawFd) -> anyhow::Result<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnixStreamExt for UnixStream {
|
||||||
|
fn claim_fd(fd: RawFd) -> anyhow::Result<Self> {
|
||||||
|
use std::os::unix::net::UnixStream as StdUnixStream;
|
||||||
|
|
||||||
|
let sock = StdUnixStream::from(claim_fd(fd)?);
|
||||||
|
sock.set_nonblocking(true)?;
|
||||||
|
Ok(UnixStream::from_std(sock))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::result::Result;
|
|
||||||
|
|
||||||
/// Try block basically…returns a result and allows the use of the question mark operator inside
|
/// Try block basically…returns a result and allows the use of the question mark operator inside
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@@ -97,3 +96,14 @@ impl<T> GuaranteedValue for Guaranteed<T> {
|
|||||||
self.unwrap()
|
self.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ensure_or<E>(b: bool, err: E) -> Result<(), E> {
|
||||||
|
match b {
|
||||||
|
true => Ok(()),
|
||||||
|
false => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bail_if<E>(b: bool, err: E) -> Result<(), E> {
|
||||||
|
ensure_or(!b, err)
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,15 +19,22 @@ pub trait IntoConst<T> {
|
|||||||
const VALUE: T;
|
const VALUE: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
struct ConstApplyNegSign<T: AssociatedUnsigned, Param: IntoConst<<T as AssociatedUnsigned>::Type>>(
|
struct ConstApplyNegSign<T: AssociatedUnsigned, Param: IntoConst<<T as AssociatedUnsigned>::Type>>(
|
||||||
*const T,
|
*const T,
|
||||||
*const Param,
|
*const Param,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
struct ConstApplyPosSign<T: AssociatedUnsigned, Param: IntoConst<<T as AssociatedUnsigned>::Type>>(
|
struct ConstApplyPosSign<T: AssociatedUnsigned, Param: IntoConst<<T as AssociatedUnsigned>::Type>>(
|
||||||
*const T,
|
*const T,
|
||||||
*const Param,
|
*const Param,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
struct ConstLshift<T, Param: IntoConst<T>, const SHIFT: i32>(*const T, *const Param); // impl IntoConst<T>
|
struct ConstLshift<T, Param: IntoConst<T>, const SHIFT: i32>(*const T, *const Param); // impl IntoConst<T>
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
struct ConstAdd<T, Lhs: IntoConst<T>, Rhs: IntoConst<T>>(*const T, *const Lhs, *const Rhs); // impl IntoConst<T>
|
struct ConstAdd<T, Lhs: IntoConst<T>, Rhs: IntoConst<T>>(*const T, *const Lhs, *const Rhs); // impl IntoConst<T>
|
||||||
|
|
||||||
/// Assigns an unsigned type to a signed type
|
/// Assigns an unsigned type to a signed type
|
||||||
|
|||||||
7
util/src/zerocopy/mod.rs
Normal file
7
util/src/zerocopy/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
mod ref_maker;
|
||||||
|
mod zerocopy_ref_ext;
|
||||||
|
mod zerocopy_slice_ext;
|
||||||
|
|
||||||
|
pub use ref_maker::*;
|
||||||
|
pub use zerocopy_ref_ext::*;
|
||||||
|
pub use zerocopy_slice_ext::*;
|
||||||
107
util/src/zerocopy/ref_maker.rs
Normal file
107
util/src/zerocopy/ref_maker.rs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use anyhow::{ensure, Context};
|
||||||
|
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use crate::zeroize::ZeroizedExt;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct RefMaker<B: Sized, T> {
|
||||||
|
buf: B,
|
||||||
|
_phantom_t: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, T> RefMaker<B, T> {
|
||||||
|
pub fn new(buf: B) -> Self {
|
||||||
|
let _phantom_t = PhantomData;
|
||||||
|
Self { buf, _phantom_t }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn target_size() -> usize {
|
||||||
|
std::mem::size_of::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_buf(self) -> B {
|
||||||
|
self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buf(&self) -> &B {
|
||||||
|
&self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buf_mut(&mut self) -> &mut B {
|
||||||
|
&mut self.buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSlice, T> RefMaker<B, T> {
|
||||||
|
pub fn parse(self) -> anyhow::Result<Ref<B, T>> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
Ref::<B, T>::new(self.buf).context("Parser error!")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_prefix_with_tail(self) -> anyhow::Result<(Self, B)> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
let (head, tail) = self.buf.split_at(Self::target_size());
|
||||||
|
Ok((Self::new(head), tail))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_prefix(self) -> anyhow::Result<(Self, Self)> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
let (head, tail) = self.buf.split_at(Self::target_size());
|
||||||
|
Ok((Self::new(head), Self::new(tail)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_prefix(self) -> anyhow::Result<Self> {
|
||||||
|
Ok(Self::from_prefix_with_tail(self)?.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_suffix_with_head(self) -> anyhow::Result<(Self, B)> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
let point = self.bytes().len() - Self::target_size();
|
||||||
|
let (head, tail) = self.buf.split_at(point);
|
||||||
|
Ok((Self::new(tail), head))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_suffix(self) -> anyhow::Result<(Self, Self)> {
|
||||||
|
self.ensure_fit()?;
|
||||||
|
let point = self.bytes().len() - Self::target_size();
|
||||||
|
let (head, tail) = self.buf.split_at(point);
|
||||||
|
Ok((Self::new(head), Self::new(tail)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_suffix(self) -> anyhow::Result<Self> {
|
||||||
|
Ok(Self::from_suffix_with_head(self)?.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes(&self) -> &[u8] {
|
||||||
|
self.buf().deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_fit(&self) -> anyhow::Result<()> {
|
||||||
|
let have = self.bytes().len();
|
||||||
|
let need = Self::target_size();
|
||||||
|
ensure!(
|
||||||
|
need <= have,
|
||||||
|
"Buffer is undersized at {have} bytes (need {need} bytes)!"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSliceMut, T> RefMaker<B, T> {
|
||||||
|
pub fn make_zeroized(self) -> anyhow::Result<Ref<B, T>> {
|
||||||
|
self.zeroized().parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_mut(&mut self) -> &mut [u8] {
|
||||||
|
self.buf_mut().deref_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSliceMut, T> Zeroize for RefMaker<B, T> {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.bytes_mut().zeroize()
|
||||||
|
}
|
||||||
|
}
|
||||||
27
util/src/zerocopy/zerocopy_ref_ext.rs
Normal file
27
util/src/zerocopy/zerocopy_ref_ext.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||||
|
|
||||||
|
pub trait ZerocopyEmancipateExt<B, T> {
|
||||||
|
fn emancipate(&self) -> Ref<&[u8], T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ZerocopyEmancipateMutExt<B, T> {
|
||||||
|
fn emancipate_mut(&mut self) -> Ref<&mut [u8], T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, T> ZerocopyEmancipateExt<B, T> for Ref<B, T>
|
||||||
|
where
|
||||||
|
B: ByteSlice,
|
||||||
|
{
|
||||||
|
fn emancipate(&self) -> Ref<&[u8], T> {
|
||||||
|
Ref::new(self.bytes()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, T> ZerocopyEmancipateMutExt<B, T> for Ref<B, T>
|
||||||
|
where
|
||||||
|
B: ByteSliceMut,
|
||||||
|
{
|
||||||
|
fn emancipate_mut(&mut self) -> Ref<&mut [u8], T> {
|
||||||
|
Ref::new(self.bytes_mut()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
39
util/src/zerocopy/zerocopy_slice_ext.rs
Normal file
39
util/src/zerocopy/zerocopy_slice_ext.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||||
|
|
||||||
|
use super::RefMaker;
|
||||||
|
|
||||||
|
pub trait ZerocopySliceExt: Sized + ByteSlice {
|
||||||
|
fn zk_ref_maker<T>(self) -> RefMaker<Self, T> {
|
||||||
|
RefMaker::<Self, T>::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zk_parse<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||||
|
self.zk_ref_maker().parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zk_parse_prefix<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||||
|
self.zk_ref_maker().from_prefix()?.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zk_parse_suffix<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||||
|
self.zk_ref_maker().from_suffix()?.parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSlice> ZerocopySliceExt for B {}
|
||||||
|
|
||||||
|
pub trait ZerocopyMutSliceExt: ZerocopySliceExt + Sized + ByteSliceMut {
|
||||||
|
fn zk_zeroized<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||||
|
self.zk_ref_maker().make_zeroized()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zk_zeroized_from_prefix<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||||
|
self.zk_ref_maker().from_prefix()?.make_zeroized()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zk_zeroized_from_suffix<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||||
|
self.zk_ref_maker().from_suffix()?.make_zeroized()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: ByteSliceMut> ZerocopyMutSliceExt for B {}
|
||||||
2
util/src/zeroize/mod.rs
Normal file
2
util/src/zeroize/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mod zeroized_ext;
|
||||||
|
pub use zeroized_ext::*;
|
||||||
10
util/src/zeroize/zeroized_ext.rs
Normal file
10
util/src/zeroize/zeroized_ext.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
pub trait ZeroizedExt: Zeroize + Sized {
|
||||||
|
fn zeroized(mut self) -> Self {
|
||||||
|
self.zeroize();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Zeroize + Sized> ZeroizedExt for T {}
|
||||||
@@ -19,7 +19,7 @@ wireguard-uapi = { workspace = true }
|
|||||||
|
|
||||||
# Socket handler only
|
# Socket handler only
|
||||||
rosenpass-to = { workspace = true }
|
rosenpass-to = { workspace = true }
|
||||||
tokio = { version = "1.38.0", features = ["sync", "full", "mio"] }
|
tokio = { version = "1.39.1", features = ["sync", "full", "mio"] }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
env_logger = { workspace = true }
|
env_logger = { workspace = true }
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ where
|
|||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let typ = res.get(0).ok_or(invalid_msg_poller())?;
|
let typ = res.first().ok_or(invalid_msg_poller())?;
|
||||||
let typ = msgs::MsgType::try_from(*typ)?;
|
let typ = msgs::MsgType::try_from(*typ)?;
|
||||||
let msgs::MsgType::SetPsk = typ; // Assert type
|
let msgs::MsgType::SetPsk = typ; // Assert type
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ where
|
|||||||
|
|
||||||
fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> {
|
fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> {
|
||||||
let config: Result<NetworkBrokerConfig, NetworkBrokerConfigErr> = config.try_into();
|
let config: Result<NetworkBrokerConfig, NetworkBrokerConfigErr> = config.try_into();
|
||||||
let config = config.map_err(|e| BrokerClientSetPskError::BrokerError(e))?;
|
let config = config.map_err(BrokerClientSetPskError::BrokerError)?;
|
||||||
|
|
||||||
use BrokerClientSetPskError::*;
|
use BrokerClientSetPskError::*;
|
||||||
const BUF_SIZE: usize = REQUEST_MSG_BUFFER_SIZE;
|
const BUF_SIZE: usize = REQUEST_MSG_BUFFER_SIZE;
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ pub struct NetworkBrokerConfig<'a> {
|
|||||||
pub psk: &'a Secret<WG_KEY_LEN>,
|
pub psk: &'a Secret<WG_KEY_LEN>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Into<SerializedBrokerConfig<'a>> for NetworkBrokerConfig<'a> {
|
impl<'a> From<NetworkBrokerConfig<'a>> for SerializedBrokerConfig<'a> {
|
||||||
fn into(self) -> SerializedBrokerConfig<'a> {
|
fn from(src: NetworkBrokerConfig<'a>) -> SerializedBrokerConfig<'a> {
|
||||||
SerializedBrokerConfig {
|
Self {
|
||||||
interface: self.iface.as_bytes(),
|
interface: src.iface.as_bytes(),
|
||||||
peer_id: self.peer_id,
|
peer_id: src.peer_id,
|
||||||
psk: self.psk,
|
psk: src.psk,
|
||||||
additional_params: &[],
|
additional_params: &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use std::result::Result;
|
|
||||||
use std::str::{from_utf8, Utf8Error};
|
use std::str::{from_utf8, Utf8Error};
|
||||||
|
|
||||||
use zerocopy::{AsBytes, FromBytes, FromZeroes};
|
use zerocopy::{AsBytes, FromBytes, FromZeroes};
|
||||||
@@ -43,7 +42,7 @@ impl SetPskRequest {
|
|||||||
self.iface_size = iface.len() as u8;
|
self.iface_size = iface.len() as u8;
|
||||||
|
|
||||||
self.iface_buf = [0; 255];
|
self.iface_buf = [0; 255];
|
||||||
(&mut self.iface_buf[..iface.len()]).copy_from_slice(iface);
|
self.iface_buf[..iface.len()].copy_from_slice(iface);
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::borrow::BorrowMut;
|
use std::borrow::BorrowMut;
|
||||||
use std::result::Result;
|
|
||||||
|
|
||||||
use rosenpass_secret_memory::{Public, Secret};
|
use rosenpass_secret_memory::{Public, Secret};
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ where
|
|||||||
) -> Result<usize, BrokerServerError> {
|
) -> Result<usize, BrokerServerError> {
|
||||||
use BrokerServerError::*;
|
use BrokerServerError::*;
|
||||||
|
|
||||||
let typ = req.get(0).ok_or(InvalidMessage)?;
|
let typ = req.first().ok_or(InvalidMessage)?;
|
||||||
let typ = msgs::MsgType::try_from(*typ)?;
|
let typ = msgs::MsgType::try_from(*typ)?;
|
||||||
let msgs::MsgType::SetPsk = typ; // Assert type
|
let msgs::MsgType::SetPsk = typ; // Assert type
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ fn main() {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub mod linux {
|
pub mod linux {
|
||||||
use std::io::{stdin, stdout, Read, Write};
|
use std::io::{stdin, stdout, Read, Write};
|
||||||
use std::result::Result;
|
|
||||||
|
|
||||||
use rosenpass_wireguard_broker::api::msgs;
|
use rosenpass_wireguard_broker::api::msgs;
|
||||||
use rosenpass_wireguard_broker::api::server::BrokerServer;
|
use rosenpass_wireguard_broker::api::server::BrokerServer;
|
||||||
@@ -60,7 +59,7 @@ pub mod linux {
|
|||||||
|
|
||||||
// Write the response
|
// Write the response
|
||||||
stdout.write_all(&(res.len() as u64).to_le_bytes())?;
|
stdout.write_all(&(res.len() as u64).to_le_bytes())?;
|
||||||
stdout.write_all(&res)?;
|
stdout.write_all(res)?;
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ async fn direct_broker_process(
|
|||||||
|
|
||||||
// Read the message itself
|
// Read the message itself
|
||||||
let mut res_buf = request; // Avoid allocating memory if we don't have to
|
let mut res_buf = request; // Avoid allocating memory if we don't have to
|
||||||
res_buf.resize(len as usize, 0);
|
res_buf.resize(len, 0);
|
||||||
stdout.read_exact(&mut res_buf[..len]).await?;
|
stdout.read_exact(&mut res_buf[..len]).await?;
|
||||||
|
|
||||||
// Return to the unix socket connection worker
|
// Return to the unix socket connection worker
|
||||||
@@ -163,7 +163,7 @@ async fn on_accept(queue: mpsc::Sender<BrokerRequest>, mut stream: UnixStream) -
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Read the message itself
|
// Read the message itself
|
||||||
req_buf.resize(len as usize, 0);
|
req_buf.resize(len, 0);
|
||||||
stream.read_exact(&mut req_buf[..len]).await?;
|
stream.read_exact(&mut req_buf[..len]).await?;
|
||||||
|
|
||||||
// Handle the message
|
// Handle the message
|
||||||
|
|||||||
@@ -52,23 +52,17 @@ impl MioBrokerClient {
|
|||||||
|
|
||||||
// This sucks
|
// This sucks
|
||||||
match self.inner.poll_response() {
|
match self.inner.poll_response() {
|
||||||
Ok(res) => {
|
Ok(res) => Ok(res),
|
||||||
return Ok(res);
|
Err(BrokerClientPollResponseError::IoError(e)) => Err(e),
|
||||||
}
|
Err(BrokerClientPollResponseError::InvalidMessage) => bail!("Invalid message"),
|
||||||
Err(BrokerClientPollResponseError::IoError(e)) => {
|
}
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
Err(BrokerClientPollResponseError::InvalidMessage) => {
|
|
||||||
bail!("Invalid message");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WireGuardBroker for MioBrokerClient {
|
impl WireGuardBroker for MioBrokerClient {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn set_psk<'a>(&mut self, config: SerializedBrokerConfig<'a>) -> anyhow::Result<()> {
|
fn set_psk(&mut self, config: SerializedBrokerConfig<'_>) -> anyhow::Result<()> {
|
||||||
use BrokerClientSetPskError::*;
|
use BrokerClientSetPskError::*;
|
||||||
let e = self.inner.set_psk(config);
|
let e = self.inner.set_psk(config);
|
||||||
match e {
|
match e {
|
||||||
@@ -115,7 +109,7 @@ impl BrokerClientIo for MioBrokerClientIo {
|
|||||||
fn send_msg(&mut self, buf: &[u8]) -> Result<(), Self::SendError> {
|
fn send_msg(&mut self, buf: &[u8]) -> Result<(), Self::SendError> {
|
||||||
self.flush()?;
|
self.flush()?;
|
||||||
self.send_or_buffer(&(buf.len() as u64).to_le_bytes())?;
|
self.send_or_buffer(&(buf.len() as u64).to_le_bytes())?;
|
||||||
self.send_or_buffer(&buf)?;
|
self.send_or_buffer(buf)?;
|
||||||
self.flush()?;
|
self.flush()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -192,7 +186,7 @@ impl MioBrokerClientIo {
|
|||||||
|
|
||||||
self.send_buf.drain(..written);
|
self.send_buf.drain(..written);
|
||||||
|
|
||||||
(&self.socket).try_io(|| (&self.socket).flush())?;
|
self.socket.try_io(|| (&self.socket).flush())?;
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -204,7 +198,7 @@ impl MioBrokerClientIo {
|
|||||||
off += raw_send(&self.socket, buf)?;
|
off += raw_send(&self.socket, buf)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.send_buf.extend((&buf[off..]).iter());
|
self.send_buf.extend(buf[off..].iter());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -231,7 +225,7 @@ fn raw_send(mut socket: &mio::net::UnixStream, data: &[u8]) -> anyhow::Result<us
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
return Ok(off);
|
Ok(off)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raw_recv(mut socket: &mio::net::UnixStream, out: &mut [u8]) -> anyhow::Result<usize> {
|
fn raw_recv(mut socket: &mio::net::UnixStream, out: &mut [u8]) -> anyhow::Result<usize> {
|
||||||
@@ -255,5 +249,5 @@ fn raw_recv(mut socket: &mio::net::UnixStream, out: &mut [u8]) -> anyhow::Result
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
return Ok(off);
|
Ok(off)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,21 +87,20 @@ impl WireGuardBroker for NetlinkWireGuardBroker {
|
|||||||
.sock
|
.sock
|
||||||
.get_device(wg::DeviceInterface::from_name(config.iface))?;
|
.get_device(wg::DeviceInterface::from_name(config.iface))?;
|
||||||
|
|
||||||
if state
|
if !state
|
||||||
.peers
|
.peers
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| &p.public_key == &config.peer_id.value)
|
.any(|p| p.public_key == config.peer_id.value)
|
||||||
.is_none()
|
|
||||||
{
|
{
|
||||||
return Err(SetPskError::NoSuchPeer);
|
return Err(SetPskError::NoSuchPeer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer update description
|
// Peer update description
|
||||||
let mut set_peer = wireguard_uapi::set::Peer::from_public_key(&config.peer_id);
|
let mut set_peer = wireguard_uapi::set::Peer::from_public_key(config.peer_id);
|
||||||
set_peer
|
set_peer
|
||||||
.flags
|
.flags
|
||||||
.push(wireguard_uapi::linux::set::WgPeerF::UpdateOnly);
|
.push(wireguard_uapi::linux::set::WgPeerF::UpdateOnly);
|
||||||
set_peer.preshared_key = Some(&config.psk.secret());
|
set_peer.preshared_key = Some(config.psk.secret());
|
||||||
|
|
||||||
// Device update description
|
// Device update description
|
||||||
let mut set_dev = wireguard_uapi::set::Device::from_ifname(config.iface);
|
let mut set_dev = wireguard_uapi::set::Device::from_ifname(config.iface);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use rosenpass_secret_memory::{Public, Secret};
|
use rosenpass_secret_memory::{Public, Secret};
|
||||||
use std::{fmt::Debug, result::Result};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub const WG_KEY_LEN: usize = 32;
|
pub const WG_KEY_LEN: usize = 32;
|
||||||
pub const WG_PEER_LEN: usize = 32;
|
pub const WG_PEER_LEN: usize = 32;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ mod integration_tests {
|
|||||||
impl WireGuardBroker for MockServerBroker {
|
impl WireGuardBroker for MockServerBroker {
|
||||||
type Error = SetPskError;
|
type Error = SetPskError;
|
||||||
|
|
||||||
|
#[allow(clippy::clone_on_copy)]
|
||||||
fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> {
|
fn set_psk(&mut self, config: SerializedBrokerConfig) -> Result<(), Self::Error> {
|
||||||
loop {
|
loop {
|
||||||
let mut lock = self.inner.try_lock();
|
let mut lock = self.inner.try_lock();
|
||||||
|
|||||||
Reference in New Issue
Block a user