mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-27 14:03:11 -08:00
Compare commits
26 Commits
dev/karo/m
...
dev/broker
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75176a66ee | ||
|
|
08cae26df0 | ||
|
|
d84efa7422 | ||
|
|
61ef5b92bb | ||
|
|
f3590645e9 | ||
|
|
184cff0e5e | ||
|
|
9819148b6f | ||
|
|
3a0ebd2cbc | ||
|
|
1eefb5f263 | ||
|
|
d45e24e9b6 | ||
|
|
972e82b35f | ||
|
|
101c9bf4b3 | ||
|
|
955d57ea49 | ||
|
|
838f700a74 | ||
|
|
5448cdc565 | ||
|
|
77cd8a9fd1 | ||
|
|
0f89ab7976 | ||
|
|
70fa9bd6d7 | ||
|
|
85a61808de | ||
|
|
cf132bca11 | ||
|
|
7bda010a9b | ||
|
|
36089fd37f | ||
|
|
31d43accd5 | ||
|
|
205c301012 | ||
|
|
d014095469 | ||
|
|
7cece82119 |
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
examples/
|
||||
target/
|
||||
flake.*
|
||||
.ci
|
||||
.direnv
|
||||
.git
|
||||
.github
|
||||
12
.github/workflows/qc.yaml
vendored
12
.github/workflows/qc.yaml
vendored
@@ -141,8 +141,10 @@ jobs:
|
||||
run: cargo install cargo-fuzz
|
||||
- name: Run fuzzing
|
||||
run: |
|
||||
cargo fuzz run fuzz_aead_enc_into -- -max_total_time=60
|
||||
cargo fuzz run fuzz_blake2b -- -max_total_time=60
|
||||
cargo fuzz run fuzz_handle_msg -- -max_total_time=60
|
||||
cargo fuzz run fuzz_kyber_encaps -- -max_total_time=60
|
||||
cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=60
|
||||
cargo fuzz run fuzz_aead_enc_into -- -max_total_time=5
|
||||
cargo fuzz run fuzz_blake2b -- -max_total_time=5
|
||||
cargo fuzz run fuzz_handle_msg -- -max_total_time=5
|
||||
ulimit -s 8192000 && RUST_MIN_STACK=33554432000 && cargo fuzz run fuzz_kyber_encaps -- -max_total_time=5
|
||||
cargo fuzz run fuzz_mceliece_encaps -- -max_total_time=5
|
||||
cargo fuzz run fuzz_box_sodium_alloc -- -max_total_time=5
|
||||
cargo fuzz run fuzz_vec_sodium_alloc -- -max_total_time=5
|
||||
|
||||
579
Cargo.lock
generated
579
Cargo.lock
generated
@@ -44,6 +44,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "anes"
|
||||
version = "0.1.6"
|
||||
@@ -85,7 +91,7 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -95,7 +101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -173,7 +179,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
"which",
|
||||
]
|
||||
|
||||
@@ -210,6 +216,12 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
@@ -293,9 +305,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.8"
|
||||
version = "4.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
|
||||
checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -303,9 +315,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.8"
|
||||
version = "4.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
|
||||
checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -322,7 +334,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -355,6 +367,16 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "command-fds"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f190f3c954f7bca3c6296d0ec561c739bdbe6c7e990294ed168d415f6e1b5b01"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core2"
|
||||
version = "0.4.0"
|
||||
@@ -442,6 +464,41 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dary_heap"
|
||||
version = "0.3.6"
|
||||
@@ -456,7 +513,38 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -492,12 +580,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.6"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -508,8 +596,8 @@ checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"windows-sys",
|
||||
"redox_syscall 0.3.5",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -523,10 +611,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.0"
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
@@ -577,9 +671,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.2"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -608,7 +702,7 @@ version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -618,10 +712,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
@@ -644,7 +744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.2",
|
||||
"hashbrown 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -655,7 +755,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.3",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -756,9 +856,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libsodium-sys-stable"
|
||||
version = "1.20.3"
|
||||
version = "1.20.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfc31f983531631496f4e621110cd81468ab78b65dee0046cfddea83caa2c327"
|
||||
checksum = "d1d164bc6f9139c5f95efb4f0be931b2bd5a9edf7e4e3c945d26b95ab8fa669b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -773,9 +873,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -828,7 +938,43 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "neli"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1805440578ced23f85145d00825c0a831e43c587132a90e100552172543ae30"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"libc",
|
||||
"log",
|
||||
"neli-proc-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "neli-proc-macros"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4"
|
||||
dependencies = [
|
||||
"either",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -850,6 +996,16 @@ dependencies = [
|
||||
"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.3",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.1"
|
||||
@@ -889,6 +1045,29 @@ version = "6.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.4.1",
|
||||
"smallvec",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
@@ -903,9 +1082,15 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
@@ -941,6 +1126,12 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.15"
|
||||
@@ -948,14 +1139,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -978,6 +1169,36 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
@@ -1007,6 +1228,15 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
@@ -1038,16 +1268,16 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.5"
|
||||
version = "0.17.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
|
||||
checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1061,21 +1291,26 @@ name = "rosenpass"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.4.8",
|
||||
"clap 4.4.10",
|
||||
"command-fds",
|
||||
"criterion",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"libsodium-sys-stable",
|
||||
"log",
|
||||
"memoffset",
|
||||
"mio",
|
||||
"oqs-sys",
|
||||
"paste",
|
||||
"rand",
|
||||
"rosenpass-cipher-traits",
|
||||
"rosenpass-ciphers",
|
||||
"rosenpass-constant-time",
|
||||
"rosenpass-lenses",
|
||||
"rosenpass-secret-memory",
|
||||
"rosenpass-sodium",
|
||||
"rosenpass-to",
|
||||
"rosenpass-util",
|
||||
"rosenpass-wireguard-broker",
|
||||
"rustix",
|
||||
"serde",
|
||||
"stacker",
|
||||
"static_assertions",
|
||||
@@ -1084,12 +1319,18 @@ dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosenpass-cipher-traits"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rosenpass-ciphers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rosenpass-constant-time",
|
||||
"rosenpass-oqs",
|
||||
"rosenpass-secret-memory",
|
||||
"rosenpass-sodium",
|
||||
"rosenpass-to",
|
||||
"static_assertions",
|
||||
@@ -1110,16 +1351,51 @@ dependencies = [
|
||||
"arbitrary",
|
||||
"libfuzzer-sys",
|
||||
"rosenpass",
|
||||
"rosenpass-cipher-traits",
|
||||
"rosenpass-ciphers",
|
||||
"rosenpass-secret-memory",
|
||||
"rosenpass-sodium",
|
||||
"rosenpass-to",
|
||||
"stacker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosenpass-lenses"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"paste",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosenpass-oqs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"oqs-sys",
|
||||
"paste",
|
||||
"rosenpass-cipher-traits",
|
||||
"rosenpass-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosenpass-secret-memory"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
"libsodium-sys-stable",
|
||||
"rand",
|
||||
"rosenpass-sodium",
|
||||
"rosenpass-to",
|
||||
"rosenpass-util",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosenpass-sodium"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"anyhow",
|
||||
"libsodium-sys-stable",
|
||||
"log",
|
||||
@@ -1140,6 +1416,25 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rosenpass-wireguard-broker"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.4.10",
|
||||
"env_logger",
|
||||
"log",
|
||||
"mio",
|
||||
"paste",
|
||||
"rosenpass-lenses",
|
||||
"rosenpass-to",
|
||||
"rosenpass-util",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"wireguard-uapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1156,22 +1451,22 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.21"
|
||||
version = "0.38.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
|
||||
checksum = "bfeae074e687625746172d639330f1de242a178bf3189b51e35a7a21573513ac"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.8"
|
||||
version = "0.21.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c"
|
||||
checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
@@ -1222,22 +1517,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.192"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.192"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1266,6 +1561,31 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
@@ -1297,6 +1617,17 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
@@ -1321,9 +1652,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
|
||||
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
@@ -1357,7 +1688,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1385,6 +1716,36 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.7.8"
|
||||
@@ -1448,9 +1809,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.8.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3"
|
||||
checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"log",
|
||||
@@ -1463,9 +1824,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@@ -1527,7 +1888,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -1549,7 +1910,7 @@ checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -1572,9 +1933,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.2"
|
||||
version = "0.25.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
|
||||
checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
@@ -1625,7 +1986,16 @@ version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1634,13 +2004,28 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"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]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1649,42 +2034,84 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.19"
|
||||
@@ -1694,6 +2121,18 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wireguard-uapi"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ba4e9811befc20af3b6efb15924a7238ee5e8e8706a196576462a00b9f1af1"
|
||||
dependencies = [
|
||||
"derive_builder",
|
||||
"libc",
|
||||
"neli",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.0.1"
|
||||
@@ -1720,7 +2159,7 @@ checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
43
Cargo.toml
43
Cargo.toml
@@ -3,16 +3,22 @@ resolver = "2"
|
||||
|
||||
members = [
|
||||
"rosenpass",
|
||||
"cipher-traits",
|
||||
"ciphers",
|
||||
"util",
|
||||
"constant-time",
|
||||
"sodium",
|
||||
"oqs",
|
||||
"to",
|
||||
"fuzz",
|
||||
"secret-memory",
|
||||
"lenses",
|
||||
"wireguard-broker",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
"rosenpass"
|
||||
"rosenpass",
|
||||
"wireguard-broker"
|
||||
]
|
||||
|
||||
[workspace.metadata.release]
|
||||
@@ -24,27 +30,38 @@ rosenpass = { path = "rosenpass" }
|
||||
rosenpass-util = { path = "util" }
|
||||
rosenpass-constant-time = { path = "constant-time" }
|
||||
rosenpass-sodium = { path = "sodium" }
|
||||
rosenpass-cipher-traits = { path = "cipher-traits" }
|
||||
rosenpass-ciphers = { path = "ciphers" }
|
||||
rosenpass-to = { path = "to" }
|
||||
rosenpass-secret-memory = { path = "secret-memory" }
|
||||
rosenpass-oqs = { path = "oqs" }
|
||||
rosenpass-lenses = { path = "lenses" }
|
||||
rosenpass-wireguard-broker = { path = "wireguard-broker" }
|
||||
criterion = "0.4.0"
|
||||
test_bin = "0.4.0"
|
||||
libfuzzer-sys = "0.4"
|
||||
stacker = "0.1.15"
|
||||
doc-comment = "0.3.3"
|
||||
base64 = "0.21.1"
|
||||
base64 = "0.21.5"
|
||||
zeroize = "1.7.0"
|
||||
memoffset = "0.9.0"
|
||||
lazy_static = "1.4.0"
|
||||
thiserror = "1.0.40"
|
||||
paste = "1.0.12"
|
||||
env_logger = "0.10.0"
|
||||
toml = "0.7.4"
|
||||
thiserror = "1.0.50"
|
||||
paste = "1.0.14"
|
||||
env_logger = "0.10.1"
|
||||
toml = "0.7.8"
|
||||
static_assertions = "1.1.0"
|
||||
log = { version = "0.4.17" }
|
||||
clap = { version = "4.3.0", features = ["derive"] }
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
allocator-api2 = "0.2.16"
|
||||
rand = "0.8.5"
|
||||
wireguard-uapi = "3.0.0"
|
||||
command-fds = "0.2.3"
|
||||
rustix = { version = "0.38.27", features = ["net"] }
|
||||
tokio = { version = "1.34.0", features = ["sync", "full", "mio"] }
|
||||
log = { version = "0.4.20" }
|
||||
clap = { version = "4.4.10", features = ["derive"] }
|
||||
serde = { version = "1.0.193", features = ["derive"] }
|
||||
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||
anyhow = { version = "1.0.71", features = ["backtrace"] }
|
||||
mio = { version = "0.8.6", features = ["net", "os-poll"] }
|
||||
libsodium-sys-stable= { version = "1.19.28", features = ["use-pkg-config"] }
|
||||
oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] }
|
||||
anyhow = { version = "1.0.75", features = ["backtrace", "std"] }
|
||||
mio = { version = "0.8.9", features = ["net", "os-poll"] }
|
||||
libsodium-sys-stable= { version = "1.20.4", features = ["use-pkg-config"] }
|
||||
oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] }
|
||||
|
||||
12
cipher-traits/Cargo.toml
Normal file
12
cipher-traits/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "rosenpass-cipher-traits"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal traits for cryptographic primitives"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[dependencies]
|
||||
5
cipher-traits/readme.md
Normal file
5
cipher-traits/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rosenpass internal libsodium bindings
|
||||
|
||||
Rosenpass internal library providing traits for cryptographic primitives.
|
||||
|
||||
This is an internal library; not guarantee is made about its API at this point in time.
|
||||
47
cipher-traits/src/kem.rs
Normal file
47
cipher-traits/src/kem.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
//! Traits and implementations for Key Encapsulation Mechanisms (KEMs)
|
||||
//!
|
||||
//! KEMs are the interface provided by almost all post-quantum
|
||||
//! secure key exchange mechanisms.
|
||||
//!
|
||||
//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting
|
||||
//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during
|
||||
//!
|
||||
//! encapsulation.
|
||||
//! The [KEM] Trait describes the basic API offered by a Key Encapsulation
|
||||
//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM].
|
||||
|
||||
use std::result::Result;
|
||||
|
||||
/// Key Encapsulation Mechanism
|
||||
///
|
||||
/// The KEM interface defines three operations: Key generation, key encapsulation and key
|
||||
/// decapsulation.
|
||||
pub trait Kem {
|
||||
type Error;
|
||||
|
||||
/// Secrete Key length
|
||||
const SK_LEN: usize;
|
||||
/// Public Key length
|
||||
const PK_LEN: usize;
|
||||
/// Ciphertext length
|
||||
const CT_LEN: usize;
|
||||
/// Shared Secret length
|
||||
const SHK_LEN: usize;
|
||||
|
||||
/// Generate a keypair consisting of secret key (`sk`) and public key (`pk`)
|
||||
///
|
||||
/// `keygen() -> sk, pk`
|
||||
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), Self::Error>;
|
||||
|
||||
/// From a public key (`pk`), generate a shared key (`shk`, for local use)
|
||||
/// and a cipher text (`ct`, to be sent to the owner of the `pk`).
|
||||
///
|
||||
/// `encaps(pk) -> shk, ct`
|
||||
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), Self::Error>;
|
||||
|
||||
/// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key
|
||||
/// (`shk`)
|
||||
///
|
||||
/// `decaps(sk, ct) -> shk`
|
||||
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), Self::Error>;
|
||||
}
|
||||
2
cipher-traits/src/lib.rs
Normal file
2
cipher-traits/src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod kem;
|
||||
pub use kem::Kem;
|
||||
@@ -14,5 +14,7 @@ anyhow = { workspace = true }
|
||||
rosenpass-sodium = { workspace = true }
|
||||
rosenpass-to = { workspace = true }
|
||||
rosenpass-constant-time = { workspace = true }
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
rosenpass-oqs = { workspace = true }
|
||||
static_assertions = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
109
ciphers/src/hash_domain.rs
Normal file
109
ciphers/src/hash_domain.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use anyhow::Result;
|
||||
use rosenpass_secret_memory::Secret;
|
||||
use rosenpass_to::To;
|
||||
|
||||
use crate::subtle::incorrect_hmac_blake2b as hash;
|
||||
|
||||
pub use hash::KEY_LEN;
|
||||
|
||||
// TODO Use a proper Dec interface
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HashDomain([u8; KEY_LEN]);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HashDomainNamespace([u8; KEY_LEN]);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretHashDomain(Secret<KEY_LEN>);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretHashDomainNamespace(Secret<KEY_LEN>);
|
||||
|
||||
impl HashDomain {
|
||||
pub fn zero() -> Self {
|
||||
Self([0u8; KEY_LEN])
|
||||
}
|
||||
|
||||
pub fn dup(self) -> HashDomainNamespace {
|
||||
HashDomainNamespace(self.0)
|
||||
}
|
||||
|
||||
pub fn turn_secret(self) -> SecretHashDomain {
|
||||
SecretHashDomain(Secret::from_slice(&self.0))
|
||||
}
|
||||
|
||||
// TODO: Protocol! Use domain separation to ensure that
|
||||
pub fn mix(self, v: &[u8]) -> Result<Self> {
|
||||
Ok(Self(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?))
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(&self.0, v.secret())
|
||||
}
|
||||
|
||||
pub fn into_value(self) -> [u8; KEY_LEN] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl HashDomainNamespace {
|
||||
pub fn mix(&self, v: &[u8]) -> Result<HashDomain> {
|
||||
Ok(HashDomain(
|
||||
hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(&self.0, v.secret())
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretHashDomain {
|
||||
pub fn invoke_primitive(k: &[u8], d: &[u8]) -> Result<SecretHashDomain> {
|
||||
let mut r = SecretHashDomain(Secret::zero());
|
||||
hash::hash(k, d).to(r.0.secret_mut())?;
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self(Secret::zero())
|
||||
}
|
||||
|
||||
pub fn dup(self) -> SecretHashDomainNamespace {
|
||||
SecretHashDomainNamespace(self.0)
|
||||
}
|
||||
|
||||
pub fn danger_from_secret(k: Secret<KEY_LEN>) -> Self {
|
||||
Self(k)
|
||||
}
|
||||
|
||||
pub fn mix(self, v: &[u8]) -> Result<SecretHashDomain> {
|
||||
Self::invoke_primitive(self.0.secret(), v)
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
Self::invoke_primitive(self.0.secret(), v.secret())
|
||||
}
|
||||
|
||||
pub fn into_secret(self) -> Secret<KEY_LEN> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn into_secret_slice(mut self, v: &[u8], dst: &[u8]) -> Result<()> {
|
||||
hash::hash(v, dst).to(self.0.secret_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretHashDomainNamespace {
|
||||
pub fn mix(&self, v: &[u8]) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(self.0.secret(), v)
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretHashDomain> {
|
||||
SecretHashDomain::invoke_primitive(self.0.secret(), v.secret())
|
||||
}
|
||||
|
||||
// TODO: This entire API is not very nice; we need this for biscuits, but
|
||||
// it might be better to extract a special "biscuit"
|
||||
// labeled subkey and reinitialize the chain with this
|
||||
pub fn danger_into_secret(self) -> Secret<KEY_LEN> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ pub mod subtle;
|
||||
pub const KEY_LEN: usize = 32;
|
||||
const_assert!(KEY_LEN == aead::KEY_LEN);
|
||||
const_assert!(KEY_LEN == xaead::KEY_LEN);
|
||||
const_assert!(KEY_LEN == hash::KEY_LEN);
|
||||
const_assert!(KEY_LEN == hash_domain::KEY_LEN);
|
||||
|
||||
/// Authenticated encryption with associated data
|
||||
pub mod aead {
|
||||
@@ -21,8 +21,9 @@ pub mod xaead {
|
||||
};
|
||||
}
|
||||
|
||||
pub mod hash {
|
||||
pub use crate::subtle::incorrect_hmac_blake2b::{
|
||||
hash, KEY_LEN, KEY_MAX, KEY_MIN, OUT_MAX, OUT_MIN,
|
||||
};
|
||||
pub mod hash_domain;
|
||||
|
||||
pub mod kem {
|
||||
pub use rosenpass_oqs::ClassicMceliece460896 as StaticKem;
|
||||
pub use rosenpass_oqs::Kyber512 as EphemeralKem;
|
||||
}
|
||||
|
||||
18
examples/broker-in-podman-container/Dockerfile
Normal file
18
examples/broker-in-podman-container/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM rust:slim as build
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
pkg-config \
|
||||
libclang-dev \
|
||||
libsodium-dev
|
||||
|
||||
WORKDIR /code
|
||||
COPY . /code
|
||||
|
||||
RUN cargo install --path rosenpass --root / --bins \
|
||||
&& cargo install --path wireguard-broker --root / --bins
|
||||
# RUN apt-get install -y libcap2-bin \
|
||||
# setcap CAP_NET_ADMIN=+eip "$(which rosenpass-wireguard-broker-privileged)"
|
||||
|
||||
CMD rosenpass
|
||||
11
examples/broker-in-podman-container/podman-compose.yml
Normal file
11
examples/broker-in-podman-container/podman-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
rosenpass:
|
||||
build:
|
||||
context: ../../
|
||||
dockerfile: ./examples/broker-in-podman-container/Dockerfile
|
||||
env:
|
||||
RUST_LOG: trace
|
||||
volumes:
|
||||
./peer-a:/config
|
||||
|
||||
@@ -291,7 +291,6 @@
|
||||
];
|
||||
buildPhase = ''
|
||||
export HOME=$(mktemp -d)
|
||||
export OSFONTDIR="$(kpsewhich --var-value TEXMF)/fonts/{opentype/public/nunito,truetype/google/noto}"
|
||||
latexmk -r tex/CI.rc
|
||||
'';
|
||||
installPhase = ''
|
||||
|
||||
@@ -11,8 +11,10 @@ cargo-fuzz = true
|
||||
arbitrary = { workspace = true }
|
||||
libfuzzer-sys = { workspace = true }
|
||||
stacker = { workspace = true }
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
rosenpass-sodium = { workspace = true }
|
||||
rosenpass-ciphers = { workspace = true }
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
rosenpass-to = { workspace = true }
|
||||
rosenpass = { workspace = true }
|
||||
|
||||
@@ -45,3 +47,15 @@ name = "fuzz_kyber_encaps"
|
||||
path = "fuzz_targets/kyber_encaps.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_box_sodium_alloc"
|
||||
path = "fuzz_targets/box_sodium_alloc.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_vec_sodium_alloc"
|
||||
path = "fuzz_targets/vec_sodium_alloc.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
12
fuzz/fuzz_targets/box_sodium_alloc.rs
Normal file
12
fuzz/fuzz_targets/box_sodium_alloc.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rosenpass_sodium::{
|
||||
alloc::{Alloc as SodiumAlloc, Box as SodiumBox},
|
||||
init,
|
||||
};
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let _ = init();
|
||||
let _ = SodiumBox::new_in(data, SodiumAlloc::new());
|
||||
});
|
||||
@@ -3,8 +3,8 @@ extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass::coloring::Secret;
|
||||
use rosenpass::protocol::CryptoServer;
|
||||
use rosenpass_secret_memory::Secret;
|
||||
use rosenpass_sodium::init as sodium_init;
|
||||
|
||||
fuzz_target!(|rx_buf: &[u8]| {
|
||||
|
||||
@@ -4,7 +4,8 @@ extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass::pqkem::{EphemeralKEM, KEM};
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::EphemeralKem;
|
||||
|
||||
#[derive(arbitrary::Arbitrary, Debug)]
|
||||
pub struct Input {
|
||||
@@ -15,5 +16,5 @@ fuzz_target!(|input: Input| {
|
||||
let mut ciphertext = [0u8; 768];
|
||||
let mut shared_secret = [0u8; 32];
|
||||
|
||||
EphemeralKEM::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap();
|
||||
EphemeralKem::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap();
|
||||
});
|
||||
|
||||
@@ -3,12 +3,13 @@ extern crate rosenpass;
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use rosenpass::pqkem::{StaticKEM, KEM};
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::StaticKem;
|
||||
|
||||
fuzz_target!(|input: &[u8]| {
|
||||
fuzz_target!(|input: [u8; StaticKem::PK_LEN]| {
|
||||
let mut ciphertext = [0u8; 188];
|
||||
let mut shared_secret = [0u8; 32];
|
||||
|
||||
// We expect errors while fuzzing therefore we do not check the result.
|
||||
let _ = StaticKEM::encaps(&mut shared_secret, &mut ciphertext, input);
|
||||
let _ = StaticKem::encaps(&mut shared_secret, &mut ciphertext, &input);
|
||||
});
|
||||
|
||||
13
fuzz/fuzz_targets/vec_sodium_alloc.rs
Normal file
13
fuzz/fuzz_targets/vec_sodium_alloc.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rosenpass_sodium::{
|
||||
alloc::{Alloc as SodiumAlloc, Vec as SodiumVec},
|
||||
init,
|
||||
};
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
let _ = init();
|
||||
let mut vec = SodiumVec::new_in(SodiumAlloc::new());
|
||||
vec.extend_from_slice(data);
|
||||
});
|
||||
16
lenses/Cargo.toml
Normal file
16
lenses/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "rosenpass-lenses"
|
||||
version = "0.1.0"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal library for parsing binary data securely"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
paste = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
3
lenses/readme.md
Normal file
3
lenses/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Rosenpass internal binary parsing library
|
||||
|
||||
This is an internal library; no guarantee is made about its API at this point in time.
|
||||
206
lenses/src/lib.rs
Normal file
206
lenses/src/lib.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use std::result::Result;
|
||||
|
||||
/// Common trait shared by all Lenses
|
||||
pub trait LenseView {
|
||||
const LEN: usize;
|
||||
}
|
||||
|
||||
/// Error during lense creation
|
||||
#[derive(thiserror::Error, Debug, Eq, PartialEq, Clone)]
|
||||
pub enum LenseError {
|
||||
#[error("buffer size mismatch")]
|
||||
BufferSizeMismatch,
|
||||
}
|
||||
|
||||
pub type LenseResult<T> = Result<T, LenseError>;
|
||||
|
||||
impl LenseError {
|
||||
pub fn ensure_exact_buffer_size(len: usize, required: usize) -> LenseResult<()> {
|
||||
(len == required)
|
||||
.then_some(())
|
||||
.ok_or(LenseError::BufferSizeMismatch)
|
||||
}
|
||||
|
||||
pub fn ensure_sufficient_buffer_size(len: usize, required: usize) -> LenseResult<()> {
|
||||
(len >= required)
|
||||
.then_some(())
|
||||
.ok_or(LenseError::BufferSizeMismatch)
|
||||
}
|
||||
}
|
||||
|
||||
/// A macro to create data lenses.
|
||||
#[macro_export]
|
||||
macro_rules! lense(
|
||||
// prefix @ offset ; optional meta ; field name : field length, ...
|
||||
(token_muncher_ref @ $offset:expr ; $( $attr:meta )* ; $field:ident : $len:expr $(, $( $tail:tt )+ )?) => {
|
||||
::paste::paste!{
|
||||
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
$( #[ $attr ] )*
|
||||
///
|
||||
#[doc = lense!(maybe_docstring_link $len)]
|
||||
/// bytes long
|
||||
pub fn $field(&self) -> &__ContainerType::Output {
|
||||
&self.0[$offset .. $offset + $len]
|
||||
}
|
||||
|
||||
/// The bytes until the
|
||||
#[doc = lense!(maybe_docstring_link Self::$field)]
|
||||
/// field
|
||||
pub fn [< until_ $field >](&self) -> &__ContainerType::Output {
|
||||
&self.0[0 .. $offset]
|
||||
}
|
||||
|
||||
// if the tail exits, consume it as well
|
||||
$(
|
||||
lense!{token_muncher_ref @ $offset + $len ; $( $tail )+ }
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
// prefix @ offset ; optional meta ; field name : field length, ...
|
||||
(token_muncher_mut @ $offset:expr ; $( $attr:meta )* ; $field:ident : $len:expr $(, $( $tail:tt )+ )?) => {
|
||||
::paste::paste!{
|
||||
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
$( #[ $attr ] )*
|
||||
///
|
||||
#[doc = lense!(maybe_docstring_link $len)]
|
||||
/// bytes long
|
||||
pub fn [< $field _mut >](&mut self) -> &mut __ContainerType::Output {
|
||||
&mut self.0[$offset .. $offset + $len]
|
||||
}
|
||||
|
||||
// if the tail exits, consume it as well
|
||||
$(
|
||||
lense!{token_muncher_mut @ $offset + $len ; $( $tail )+ }
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
// switch that yields literals unchanged, but creates docstring links to
|
||||
// constants
|
||||
// TODO the doc string link doesn't work if $x is taken from a generic,
|
||||
(maybe_docstring_link $x:literal) => (stringify!($x));
|
||||
(maybe_docstring_link $x:expr) => (stringify!([$x]));
|
||||
|
||||
// struct name < optional generics > := optional doc string field name : field length, ...
|
||||
($type:ident $( < $( $generic:ident ),+ > )? := $( $( #[ $attr:meta ] )* $field:ident : $len:expr ),+) => (::paste::paste!{
|
||||
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
/// A data lense to manipulate byte slices.
|
||||
///
|
||||
//// # Fields
|
||||
///
|
||||
$(
|
||||
/// - `
|
||||
#[doc = stringify!($field)]
|
||||
/// `:
|
||||
#[doc = lense!(maybe_docstring_link $len)]
|
||||
/// bytes
|
||||
)+
|
||||
pub struct $type<__ContainerType $(, $( $generic ),+ )? > (
|
||||
__ContainerType,
|
||||
// The phantom data is required, since all generics declared on a
|
||||
// type need to be used on the type.
|
||||
// https://doc.rust-lang.org/stable/error_codes/E0392.html
|
||||
$( $( ::core::marker::PhantomData<$generic> ),+ )?
|
||||
);
|
||||
|
||||
impl<__ContainerType $(, $( $generic: LenseView ),+ )? > $type<__ContainerType $(, $( $generic ),+ )? >{
|
||||
$(
|
||||
/// Size in bytes of the field `
|
||||
#[doc = !($field)]
|
||||
/// `
|
||||
pub const fn [< $field _len >]() -> usize{
|
||||
$len
|
||||
}
|
||||
)+
|
||||
|
||||
/// Verify that `len` exactly holds [Self]
|
||||
pub fn check_size(len: usize) -> ::rosenpass_lenses::LenseResult<()> {
|
||||
::rosenpass_lenses::LenseError::ensure_exact_buffer_size(len, $( $len + )+ 0)
|
||||
}
|
||||
}
|
||||
|
||||
// read-only accessor functions
|
||||
impl<'a, __ContainerType $(, $( $generic: LenseView ),+ )?> $type<&'a __ContainerType $(, $( $generic ),+ )?>
|
||||
where
|
||||
__ContainerType: std::ops::Index<std::ops::Range<usize>> + ?Sized,
|
||||
{
|
||||
lense!{token_muncher_ref @ 0 ; $( $( $attr )* ; $field : $len ),+ }
|
||||
|
||||
/// View into all bytes belonging to this Lense
|
||||
pub fn all_bytes(&self) -> &__ContainerType::Output {
|
||||
&self.0[0..Self::LEN]
|
||||
}
|
||||
}
|
||||
|
||||
// mutable accessor functions
|
||||
impl<'a, __ContainerType $(, $( $generic: LenseView ),+ )?> $type<&'a mut __ContainerType $(, $( $generic ),+ )?>
|
||||
where
|
||||
__ContainerType: std::ops::IndexMut<std::ops::Range<usize>> + ?Sized,
|
||||
{
|
||||
lense!{token_muncher_ref @ 0 ; $( $( $attr )* ; $field : $len ),+ }
|
||||
lense!{token_muncher_mut @ 0 ; $( $( $attr )* ; $field : $len ),+ }
|
||||
|
||||
/// View into all bytes belonging to this Lense
|
||||
pub fn all_bytes(&self) -> &__ContainerType::Output {
|
||||
&self.0[0..Self::LEN]
|
||||
}
|
||||
|
||||
/// View into all bytes belonging to this Lense
|
||||
pub fn all_bytes_mut(&mut self) -> &mut __ContainerType::Output {
|
||||
&mut self.0[0..Self::LEN]
|
||||
}
|
||||
}
|
||||
|
||||
// lense trait, allowing us to know the implementing lenses size
|
||||
impl<__ContainerType $(, $( $generic: LenseView ),+ )? > LenseView for $type<__ContainerType $(, $( $generic ),+ )? >{
|
||||
/// Number of bytes required to store this type in binary format
|
||||
const LEN: usize = $( $len + )+ 0;
|
||||
}
|
||||
|
||||
/// Extension trait to allow checked creation of a lense over
|
||||
/// some byte slice that contains a
|
||||
#[doc = lense!(maybe_docstring_link $type)]
|
||||
pub trait [< $type Ext >] {
|
||||
type __ContainerType;
|
||||
|
||||
/// Create a lense to the byte slice
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >>;
|
||||
|
||||
/// Create a lense to the byte slice, automatically truncating oversized buffers
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >>;
|
||||
}
|
||||
|
||||
impl<'a> [< $type Ext >] for &'a [u8] {
|
||||
type __ContainerType = &'a [u8];
|
||||
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >> {
|
||||
$type::<Self::__ContainerType, $( $($generic),+ )? >::check_size(self.len())?;
|
||||
Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? ))
|
||||
}
|
||||
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >> {
|
||||
let required_size = $( $len + )+ 0;
|
||||
::rosenpass_lenses::LenseError::ensure_sufficient_buffer_size(self.len(), required_size)?;
|
||||
[< $type Ext >]::[< $type:snake >](&self[..required_size])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> [< $type Ext >] for &'a mut [u8] {
|
||||
type __ContainerType = &'a mut [u8];
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >> {
|
||||
$type::<Self::__ContainerType, $( $($generic),+ )? >::check_size(self.len())?;
|
||||
Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? ))
|
||||
}
|
||||
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> ::rosenpass_lenses::LenseResult< $type<Self::__ContainerType, $( $($generic),+ )? >> {
|
||||
let required_size = $( $len + )+ 0;
|
||||
::rosenpass_lenses::LenseError::ensure_sufficient_buffer_size(self.len(), required_size)?;
|
||||
[< $type Ext >]::[< $type:snake >](&mut self[..required_size])
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
16
oqs/Cargo.toml
Normal file
16
oqs/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "rosenpass-oqs"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal bindings to liboqs"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[dependencies]
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
rosenpass-util = { workspace = true }
|
||||
oqs-sys = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
5
oqs/readme.md
Normal file
5
oqs/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rosenpass internal liboqs bindings
|
||||
|
||||
Rosenpass internal library providing bindings to liboqs.
|
||||
|
||||
This is an internal library; not guarantee is made about its API at this point in time.
|
||||
80
oqs/src/kem_macro.rs
Normal file
80
oqs/src/kem_macro.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
macro_rules! oqs_kem {
|
||||
($name:ident) => { ::paste::paste!{
|
||||
mod [< $name:snake >] {
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_util::result::Guaranteed;
|
||||
|
||||
pub enum [< $name:camel >] {}
|
||||
|
||||
/// # Panic & Safety
|
||||
///
|
||||
/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte
|
||||
/// slices only identified using raw pointers. It must be ensured that the raw
|
||||
/// pointers point into byte slices of sufficient length, to avoid UB through
|
||||
/// overwriting of arbitrary data. This is ensured through assertions in the
|
||||
/// implementation.
|
||||
///
|
||||
/// __Note__: This requirement is stricter than necessary, it would suffice
|
||||
/// to only check that the buffers are big enough, allowing them to be even
|
||||
/// bigger. However, from a correctness point of view it does not make sense to
|
||||
/// allow bigger buffers.
|
||||
impl Kem for [< $name:camel >] {
|
||||
type Error = ::std::convert::Infallible;
|
||||
|
||||
const SK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_secret_key >] as usize;
|
||||
const PK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_public_key >] as usize;
|
||||
const CT_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_ciphertext >] as usize;
|
||||
const SHK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_shared_secret >] as usize;
|
||||
|
||||
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Guaranteed<()> {
|
||||
assert_eq!(sk.len(), Self::SK_LEN);
|
||||
assert_eq!(pk.len(), Self::PK_LEN);
|
||||
unsafe {
|
||||
oqs_call!(
|
||||
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ keypair >],
|
||||
pk.as_mut_ptr(),
|
||||
sk.as_mut_ptr()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Guaranteed<()> {
|
||||
assert_eq!(shk.len(), Self::SHK_LEN);
|
||||
assert_eq!(ct.len(), Self::CT_LEN);
|
||||
assert_eq!(pk.len(), Self::PK_LEN);
|
||||
unsafe {
|
||||
oqs_call!(
|
||||
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ encaps >],
|
||||
ct.as_mut_ptr(),
|
||||
shk.as_mut_ptr(),
|
||||
pk.as_ptr()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Guaranteed<()> {
|
||||
assert_eq!(shk.len(), Self::SHK_LEN);
|
||||
assert_eq!(sk.len(), Self::SK_LEN);
|
||||
assert_eq!(ct.len(), Self::CT_LEN);
|
||||
unsafe {
|
||||
oqs_call!(
|
||||
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ decaps >],
|
||||
shk.as_mut_ptr(),
|
||||
ct.as_ptr(),
|
||||
sk.as_ptr()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub use [< $name:snake >] :: [< $name:camel >];
|
||||
}}
|
||||
}
|
||||
21
oqs/src/lib.rs
Normal file
21
oqs/src/lib.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
macro_rules! oqs_call {
|
||||
($name:path, $($args:expr),*) => {{
|
||||
use oqs_sys::common::OQS_STATUS::*;
|
||||
|
||||
match $name($($args),*) {
|
||||
OQS_SUCCESS => {}, // nop
|
||||
OQS_EXTERNAL_LIB_ERROR_OPENSSL => {
|
||||
panic!("OpenSSL error in liboqs' {}.", stringify!($name));
|
||||
},
|
||||
OQS_ERROR => {
|
||||
panic!("Unknown error in liboqs' {}.", stringify!($name));
|
||||
}
|
||||
}
|
||||
}};
|
||||
($name:ident) => { oqs_call!($name, ) };
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod kem_macro;
|
||||
oqs_kem!(kyber_512);
|
||||
oqs_kem!(classic_mceliece_460896);
|
||||
@@ -177,7 +177,11 @@ version={4.0},
|
||||
\titlehead{\centerline{\includegraphics[width=4cm]{RosenPass-Logo}}}
|
||||
\title{\inserttitle}
|
||||
}
|
||||
\author{\csname insertauthor\endcsname}
|
||||
\ifx\csname insertauthor\endcsname\relax
|
||||
\author{}
|
||||
\else
|
||||
\author{\parbox{\linewidth}{\centering\insertauthor}}
|
||||
\fi
|
||||
\subject{\csname insertsubject\endcsname}
|
||||
\date{\vspace{-1cm}}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "rosenpass"
|
||||
description = "Build post-quantum-secure VPNs with WireGuard!"
|
||||
version = "0.2.1"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Build post-quantum-secure VPNs with WireGuard!"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
@@ -18,13 +18,15 @@ rosenpass-util = { workspace = true }
|
||||
rosenpass-constant-time = { workspace = true }
|
||||
rosenpass-sodium = { workspace = true }
|
||||
rosenpass-ciphers = { workspace = true }
|
||||
rosenpass-cipher-traits = { workspace = true }
|
||||
rosenpass-to = { workspace = true }
|
||||
rosenpass-secret-memory = { workspace = true }
|
||||
rosenpass-lenses = { workspace = true }
|
||||
rosenpass-wireguard-broker = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
static_assertions = { workspace = true }
|
||||
memoffset = { workspace = true }
|
||||
libsodium-sys-stable = { workspace = true }
|
||||
oqs-sys = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
log = { workspace = true }
|
||||
@@ -33,6 +35,9 @@ serde = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
mio = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
command-fds = { workspace = true }
|
||||
rustix = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
@@ -1,38 +1,26 @@
|
||||
use anyhow::bail;
|
||||
|
||||
use anyhow::Result;
|
||||
use log::{debug, error, info, warn};
|
||||
use mio::Interest;
|
||||
use mio::Token;
|
||||
use rosenpass_util::file::fopen_w;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::io::Write;
|
||||
|
||||
use std::io::ErrorKind;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::Ipv6Addr;
|
||||
use std::net::SocketAddr;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::net::SocketAddrV6;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::io::{ErrorKind, Write};
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use std::slice;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{
|
||||
config::Verbosity,
|
||||
protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing},
|
||||
};
|
||||
use rosenpass_util::attempt;
|
||||
use anyhow::{bail, Result};
|
||||
use log::{error, info, warn};
|
||||
use mio::{Interest, Token};
|
||||
|
||||
use rosenpass_secret_memory::Public;
|
||||
use rosenpass_util::b64::{b64_writer, fmt_b64};
|
||||
use rosenpass_util::{attempt, file::fopen_w};
|
||||
use rosenpass_wireguard_broker::api::mio_client::MioBrokerClient as PskBroker;
|
||||
use rosenpass_wireguard_broker::WireGuardBroker;
|
||||
|
||||
use crate::config::Verbosity;
|
||||
use crate::protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing};
|
||||
|
||||
const IPV4_ANY_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
|
||||
const IPV6_ANY_ADDR: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
fn ipv4_any_binding() -> SocketAddr {
|
||||
// addr, port
|
||||
SocketAddr::V4(SocketAddrV4::new(IPV4_ANY_ADDR, 0))
|
||||
@@ -43,6 +31,19 @@ fn ipv6_any_binding() -> SocketAddr {
|
||||
SocketAddr::V6(SocketAddrV6::new(IPV6_ANY_ADDR, 0, 0, 0))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MioTokenDispenser {
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
impl MioTokenDispenser {
|
||||
fn get_token(&mut self) -> Token {
|
||||
let r = self.counter;
|
||||
self.counter += 1;
|
||||
Token(r)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct AppPeer {
|
||||
pub outfile: Option<PathBuf>,
|
||||
@@ -59,14 +60,24 @@ impl AppPeer {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct WireguardOut {
|
||||
// impl KeyOutput
|
||||
pub dev: String,
|
||||
pub pk: String,
|
||||
pub pk: Public<32>,
|
||||
pub extra_params: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for WireguardOut {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dev: Default::default(),
|
||||
pk: Public::zero(),
|
||||
extra_params: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds the state of the application, namely the external IO
|
||||
///
|
||||
/// Responsible for file IO, network IO
|
||||
@@ -77,6 +88,7 @@ pub struct AppServer {
|
||||
pub sockets: Vec<mio::net::UdpSocket>,
|
||||
pub events: mio::Events,
|
||||
pub mio_poll: mio::Poll,
|
||||
pub psk_broker: RefCell<PskBroker>,
|
||||
pub peers: Vec<AppPeer>,
|
||||
pub verbosity: Verbosity,
|
||||
pub all_sockets_drained: bool,
|
||||
@@ -341,11 +353,24 @@ impl AppServer {
|
||||
sk: SSk,
|
||||
pk: SPk,
|
||||
addrs: Vec<SocketAddr>,
|
||||
psk_broker_socket: UnixStream,
|
||||
verbosity: Verbosity,
|
||||
) -> anyhow::Result<Self> {
|
||||
// setup mio
|
||||
let mio_poll = mio::Poll::new()?;
|
||||
let events = mio::Events::with_capacity(8);
|
||||
let mut dispenser = MioTokenDispenser::default();
|
||||
|
||||
// Create the Wireguard broker connection
|
||||
let psk_broker = {
|
||||
let mut sock = mio::net::UnixStream::from_std(psk_broker_socket);
|
||||
mio_poll.registry().register(
|
||||
&mut sock,
|
||||
dispenser.get_token(),
|
||||
Interest::READABLE | Interest::WRITABLE,
|
||||
)?;
|
||||
PskBroker::new(sock)
|
||||
};
|
||||
|
||||
// bind each SocketAddr to a socket
|
||||
let maybe_sockets: Result<Vec<_>, _> =
|
||||
@@ -430,6 +455,7 @@ impl AppServer {
|
||||
Ok(Self {
|
||||
crypt: CryptoServer::new(sk, pk),
|
||||
peers: Vec::new(),
|
||||
psk_broker: RefCell::new(psk_broker),
|
||||
verbosity,
|
||||
sockets,
|
||||
events,
|
||||
@@ -624,31 +650,9 @@ impl AppServer {
|
||||
}
|
||||
|
||||
if let Some(owg) = ap.outwg.as_ref() {
|
||||
let mut child = Command::new("wg")
|
||||
.arg("set")
|
||||
.arg(&owg.dev)
|
||||
.arg("peer")
|
||||
.arg(&owg.pk)
|
||||
.arg("preshared-key")
|
||||
.arg("/dev/stdin")
|
||||
.stdin(Stdio::piped())
|
||||
.args(&owg.extra_params)
|
||||
.spawn()?;
|
||||
b64_writer(child.stdin.take().unwrap()).write_all(key.secret())?;
|
||||
|
||||
thread::spawn(move || {
|
||||
let status = child.wait();
|
||||
|
||||
if let Ok(status) = status {
|
||||
if status.success() {
|
||||
debug!("successfully passed psk to wg")
|
||||
} else {
|
||||
error!("could not pass psk to wg {:?}", status)
|
||||
}
|
||||
} else {
|
||||
error!("wait failed: {:?}", status)
|
||||
}
|
||||
});
|
||||
self.psk_broker
|
||||
.borrow_mut()
|
||||
.set_psk(&owg.dev, owg.pk.value, *key.secret())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -706,9 +710,16 @@ impl AppServer {
|
||||
|
||||
// only poll if we drained all sockets before
|
||||
if self.all_sockets_drained {
|
||||
self.mio_poll.poll(&mut self.events, Some(timeout))?;
|
||||
self.mio_poll
|
||||
.poll(&mut self.events, Some(timeout))
|
||||
.or_else(|e| match e.kind() {
|
||||
ErrorKind::Interrupted | ErrorKind::WouldBlock => Ok(()),
|
||||
_ => Err(e),
|
||||
})?;
|
||||
}
|
||||
|
||||
self.psk_broker.get_mut().poll()?;
|
||||
|
||||
let mut would_block_count = 0;
|
||||
for (sock_no, socket) in self.sockets.iter_mut().enumerate() {
|
||||
match socket.recv_from(buf) {
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
use anyhow::{bail, ensure};
|
||||
use std::io::{BufReader, Read};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::thread;
|
||||
|
||||
use anyhow::{bail, ensure, Context};
|
||||
use clap::Parser;
|
||||
use command_fds::{CommandFdExt, FdMapping};
|
||||
use log::{error, info};
|
||||
use rustix::fd::AsRawFd;
|
||||
use rustix::net::{socketpair, AddressFamily, SocketFlags, SocketType};
|
||||
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::StaticKem;
|
||||
use rosenpass_secret_memory::file::StoreSecret;
|
||||
use rosenpass_secret_memory::Public;
|
||||
use rosenpass_util::b64::b64_reader;
|
||||
use rosenpass_util::file::{LoadValue, LoadValueB64};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::app_server;
|
||||
use crate::app_server::AppServer;
|
||||
use crate::{
|
||||
// app_server::{AppServer, LoadValue, LoadValueB64},
|
||||
coloring::Secret,
|
||||
pqkem::{StaticKEM, KEM},
|
||||
protocol::{SPk, SSk, SymKey},
|
||||
};
|
||||
use crate::protocol::{SPk, SSk, SymKey};
|
||||
|
||||
use super::config;
|
||||
|
||||
@@ -64,6 +74,7 @@ pub enum Cli {
|
||||
config_file: PathBuf,
|
||||
|
||||
/// Forcefully overwrite existing config file
|
||||
/// - [ ] Janepie
|
||||
#[clap(short, long)]
|
||||
force: bool,
|
||||
},
|
||||
@@ -89,6 +100,15 @@ pub enum Cli {
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// Deprecated - use gen-keys instead
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
#[allow(rustdoc::invalid_html_tags)]
|
||||
Keygen {
|
||||
// NOTE yes, the legacy keygen argument initially really accepted "privet-key", not "secret-key"!
|
||||
/// public-key <PATH> private-key <PATH>
|
||||
args: Vec<String>,
|
||||
},
|
||||
|
||||
/// Validate a configuration
|
||||
Validate { config_files: Vec<PathBuf> },
|
||||
|
||||
@@ -121,6 +141,40 @@ impl Cli {
|
||||
config::Rosenpass::example_config().store(config_file)?;
|
||||
}
|
||||
|
||||
// Deprecated - use gen-keys instead
|
||||
Keygen { args } => {
|
||||
log::warn!("The 'keygen' command is deprecated. Please use the 'gen-keys' command instead.");
|
||||
|
||||
let mut public_key: Option<PathBuf> = None;
|
||||
let mut secret_key: Option<PathBuf> = None;
|
||||
|
||||
// Manual arg parsing, since clap wants to prefix flags with "--"
|
||||
let mut args = args.into_iter();
|
||||
loop {
|
||||
match (args.next().as_ref().map(String::as_str), args.next()) {
|
||||
(Some("private-key"), Some(opt)) | (Some("secret-key"), Some(opt)) => {
|
||||
secret_key = Some(opt.into());
|
||||
}
|
||||
(Some("public-key"), Some(opt)) => {
|
||||
public_key = Some(opt.into());
|
||||
}
|
||||
(Some(flag), _) => {
|
||||
bail!("Unknown option `{}`", flag);
|
||||
}
|
||||
(_, _) => break,
|
||||
};
|
||||
}
|
||||
|
||||
if secret_key.is_none() {
|
||||
bail!("private-key is required");
|
||||
}
|
||||
if public_key.is_none() {
|
||||
bail!("public-key is required");
|
||||
}
|
||||
|
||||
generate_and_save_keypair(secret_key.unwrap(), public_key.unwrap())?;
|
||||
}
|
||||
|
||||
GenKeys {
|
||||
config_file,
|
||||
public_key,
|
||||
@@ -162,12 +216,7 @@ impl Cli {
|
||||
}
|
||||
|
||||
// generate the keys and store them in files
|
||||
let mut ssk = crate::protocol::SSk::random();
|
||||
let mut spk = crate::protocol::SPk::random();
|
||||
StaticKEM::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
||||
|
||||
ssk.store_secret(skf)?;
|
||||
spk.store_secret(pkf)?;
|
||||
generate_and_save_keypair(skf, pkf)?;
|
||||
}
|
||||
|
||||
ExchangeConfig { config_file } => {
|
||||
@@ -222,11 +271,53 @@ impl Cli {
|
||||
let sk = SSk::load(&config.secret_key)?;
|
||||
let pk = SPk::load(&config.public_key)?;
|
||||
|
||||
// Spawn the psk broker and use socketpair(2) to connect with them
|
||||
let psk_broker_socket = {
|
||||
let (ours, theirs) = socketpair(
|
||||
AddressFamily::UNIX,
|
||||
SocketType::STREAM,
|
||||
SocketFlags::empty(),
|
||||
None,
|
||||
)?;
|
||||
|
||||
// Setup our end of the socketpair
|
||||
let ours = UnixStream::from(ours);
|
||||
ours.set_nonblocking(true)?;
|
||||
|
||||
// Start the PSK broker
|
||||
let mut child = Command::new("rosenpass-wireguard-broker-socket-handler")
|
||||
.args(["--stream-fd", "3"])
|
||||
.fd_mappings(vec![FdMapping {
|
||||
parent_fd: theirs.as_raw_fd(),
|
||||
child_fd: 3,
|
||||
}])?
|
||||
.spawn()?;
|
||||
|
||||
// Handle the PSK broker crashing
|
||||
thread::spawn(move || {
|
||||
let status = child.wait();
|
||||
|
||||
if let Ok(status) = status {
|
||||
if status.success() {
|
||||
// Maybe they are doing double forking?
|
||||
info!("PSK broker exited.");
|
||||
} else {
|
||||
error!("PSK broker exited with an error ({status:?})");
|
||||
}
|
||||
} else {
|
||||
error!("Wait on PSK broker process failed ({status:?})");
|
||||
}
|
||||
});
|
||||
|
||||
ours
|
||||
};
|
||||
|
||||
// start an application server
|
||||
let mut srv = std::boxed::Box::<AppServer>::new(AppServer::new(
|
||||
sk,
|
||||
pk,
|
||||
config.listen,
|
||||
psk_broker_socket,
|
||||
config.verbosity,
|
||||
)?);
|
||||
|
||||
@@ -236,11 +327,24 @@ impl Cli {
|
||||
cfg_peer.pre_shared_key.map(SymKey::load_b64).transpose()?,
|
||||
SPk::load(&cfg_peer.public_key)?,
|
||||
cfg_peer.key_out,
|
||||
cfg_peer.wg.map(|cfg| app_server::WireguardOut {
|
||||
dev: cfg.device,
|
||||
pk: cfg.peer,
|
||||
extra_params: cfg.extra_params,
|
||||
}),
|
||||
cfg_peer
|
||||
.wg
|
||||
.map(|cfg| -> anyhow::Result<_> {
|
||||
let b64pk = &cfg.peer;
|
||||
let mut pk = Public::zero();
|
||||
b64_reader(BufReader::new(b64pk.as_bytes()))
|
||||
.read_exact(&mut pk.value)
|
||||
.with_context(|| {
|
||||
format!("Could not decode base64 public key: '{b64pk}'")
|
||||
})?;
|
||||
|
||||
Ok(app_server::WireguardOut {
|
||||
pk,
|
||||
dev: cfg.device,
|
||||
extra_params: cfg.extra_params,
|
||||
})
|
||||
})
|
||||
.transpose()?,
|
||||
cfg_peer.endpoint.clone(),
|
||||
)?;
|
||||
}
|
||||
@@ -249,13 +353,11 @@ impl Cli {
|
||||
}
|
||||
}
|
||||
|
||||
trait StoreSecret {
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()>;
|
||||
}
|
||||
|
||||
impl<const N: usize> StoreSecret for Secret<N> {
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||
std::fs::write(path, self.secret())?;
|
||||
Ok(())
|
||||
}
|
||||
/// generate secret and public keys, store in files according to the paths passed as arguments
|
||||
fn generate_and_save_keypair(secret_key: PathBuf, public_key: PathBuf) -> anyhow::Result<()> {
|
||||
let mut ssk = crate::protocol::SSk::random();
|
||||
let mut spk = crate::protocol::SPk::random();
|
||||
StaticKem::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
||||
ssk.store_secret(secret_key)?;
|
||||
spk.store_secret(public_key)
|
||||
}
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
//! Types types for dealing with (secret-) values
|
||||
//!
|
||||
//! These types use type level coloring to make accidential leackage of secrets extra hard. Both [Secret] and [Public] own their data, but the memory backing
|
||||
//! [Secret] is special:
|
||||
//! - as it is heap allocated, we can actively zeroize the memory before freeing it.
|
||||
//! - guard pages before and after each allocation trap accidential sequential reads that creep towards our secrets
|
||||
//! - the memory is mlocked, e.g. it is never swapped
|
||||
|
||||
use anyhow::Context;
|
||||
use lazy_static::lazy_static;
|
||||
use libsodium_sys as libsodium;
|
||||
use rosenpass_util::{
|
||||
b64::b64_reader,
|
||||
file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd, StoreValue},
|
||||
functional::mutating,
|
||||
mem::cpy,
|
||||
};
|
||||
use std::result::Result;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
convert::TryInto,
|
||||
fmt,
|
||||
ops::{Deref, DerefMut},
|
||||
os::raw::c_void,
|
||||
path::Path,
|
||||
ptr::null_mut,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
// This might become a problem in library usage; it's effectively a memory
|
||||
// leak which probably isn't a problem right now because most memory will
|
||||
// be reused…
|
||||
lazy_static! {
|
||||
static ref SECRET_CACHE: Mutex<SecretMemoryPool> = Mutex::new(SecretMemoryPool::new());
|
||||
}
|
||||
|
||||
/// Pool that stores secret memory allocations
|
||||
///
|
||||
/// Allocation of secret memory is expensive. Thus, this struct provides a
|
||||
/// pool of secret memory, readily available to yield protected, slices of
|
||||
/// memory.
|
||||
///
|
||||
/// Further information about the protection in place can be found in in the
|
||||
/// [libsodium documentation](https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations)
|
||||
#[derive(Debug)] // TODO check on Debug derive, is that clever
|
||||
pub struct SecretMemoryPool {
|
||||
pool: HashMap<usize, Vec<*mut c_void>>,
|
||||
}
|
||||
|
||||
impl SecretMemoryPool {
|
||||
/// Create a new [SecretMemoryPool]
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
let pool = HashMap::new();
|
||||
|
||||
Self { pool }
|
||||
}
|
||||
|
||||
/// Return secrete back to the pool for future re-use
|
||||
///
|
||||
/// This consumes the [Secret], but its memory is re-used.
|
||||
pub fn release<const N: usize>(&mut self, mut s: Secret<N>) {
|
||||
unsafe {
|
||||
self.release_by_ref(&mut s);
|
||||
}
|
||||
std::mem::forget(s);
|
||||
}
|
||||
|
||||
/// Return secret back to the pool for future re-use, by slice
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// After calling this function on a [Secret], the secret must never be
|
||||
/// used again for anything.
|
||||
unsafe fn release_by_ref<const N: usize>(&mut self, s: &mut Secret<N>) {
|
||||
s.zeroize();
|
||||
let Secret { ptr: secret } = s;
|
||||
// don't call Secret::drop, that could cause a double free
|
||||
self.pool.entry(N).or_default().push(*secret);
|
||||
}
|
||||
|
||||
/// Take protected memory from the pool, allocating new one if no suitable
|
||||
/// chunk is found in the inventory.
|
||||
///
|
||||
/// The secret is guaranteed to be full of nullbytes
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function contains an unsafe call to [libsodium::sodium_malloc].
|
||||
/// This call has no known safety invariants, thus nothing can go wrong™.
|
||||
/// However, just like normal `malloc()` this can return a null ptr. Thus
|
||||
/// the returned pointer is checked for null; causing the program to panic
|
||||
/// if it is null.
|
||||
pub fn take<const N: usize>(&mut self) -> Secret<N> {
|
||||
let entry = self.pool.entry(N).or_default();
|
||||
let secret = entry.pop().unwrap_or_else(|| {
|
||||
let ptr = unsafe { libsodium::sodium_malloc(N) };
|
||||
assert!(
|
||||
!ptr.is_null(),
|
||||
"libsodium::sodium_mallloc() returned a null ptr"
|
||||
);
|
||||
ptr
|
||||
});
|
||||
|
||||
let mut s = Secret { ptr: secret };
|
||||
s.zeroize();
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SecretMemoryPool {
|
||||
/// # Safety
|
||||
///
|
||||
/// The drop implementation frees the contained elements using
|
||||
/// [libsodium::sodium_free]. This is safe as long as every `*mut c_void`
|
||||
/// contained was initialized with a call to [libsodium::sodium_malloc]
|
||||
fn drop(&mut self) {
|
||||
for ptr in self.pool.drain().flat_map(|(_, x)| x.into_iter()) {
|
||||
unsafe {
|
||||
libsodium::sodium_free(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// No safety implications are known, since the `*mut c_void` in
|
||||
/// is essentially used like a `&mut u8` [SecretMemoryPool].
|
||||
unsafe impl Send for SecretMemoryPool {}
|
||||
|
||||
/// Store for a secret
|
||||
///
|
||||
/// Uses memory allocated with [libsodium::sodium_malloc],
|
||||
/// esentially can do the same things as `[u8; N].as_mut_ptr()`.
|
||||
pub struct Secret<const N: usize> {
|
||||
ptr: *mut c_void,
|
||||
}
|
||||
|
||||
impl<const N: usize> Clone for Secret<N> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut new = Self::zero();
|
||||
new.secret_mut().clone_from_slice(self.secret());
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Drop for Secret<N> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
// the invariant that the [Secret] is not used after the
|
||||
// `release_by_ref` call is guaranteed, since this is a drop implementation
|
||||
unsafe { SECRET_CACHE.lock().unwrap().release_by_ref(self) };
|
||||
self.ptr = null_mut();
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Secret<N> {
|
||||
pub fn from_slice(slice: &[u8]) -> Self {
|
||||
let mut new_self = Self::zero();
|
||||
new_self.secret_mut().copy_from_slice(slice);
|
||||
new_self
|
||||
}
|
||||
|
||||
/// Returns a new [Secret] that is zero initialized
|
||||
pub fn zero() -> Self {
|
||||
// Using [SecretMemoryPool] here because this operation is expensive,
|
||||
// yet it is used in hot loops
|
||||
let s = SECRET_CACHE.lock().unwrap().take();
|
||||
assert_eq!(s.secret(), &[0u8; N]);
|
||||
s
|
||||
}
|
||||
|
||||
/// Returns a new [Secret] that is randomized
|
||||
pub fn random() -> Self {
|
||||
mutating(Self::zero(), |r| r.randomize())
|
||||
}
|
||||
|
||||
/// Sets all data of an existing secret to null bytes
|
||||
pub fn zeroize(&mut self) {
|
||||
rosenpass_sodium::helpers::memzero(self.secret_mut());
|
||||
}
|
||||
|
||||
/// Sets all data an existing secret to random bytes
|
||||
pub fn randomize(&mut self) {
|
||||
rosenpass_sodium::helpers::randombytes_buf(self.secret_mut());
|
||||
}
|
||||
|
||||
/// Borrows the data
|
||||
pub fn secret(&self) -> &[u8; N] {
|
||||
// - calling `from_raw_parts` is safe, because `ptr` is initalized with
|
||||
// as `N` byte allocation from the creation of `Secret` onwards. `ptr`
|
||||
// stays valid over the full lifetime of `Secret`
|
||||
//
|
||||
// - calling uwnrap is safe, because we can guarantee that the slice has
|
||||
// exactly the required size `N` to create an array of `N` elements.
|
||||
let ptr = self.ptr as *const u8;
|
||||
let slice = unsafe { std::slice::from_raw_parts(ptr, N) };
|
||||
slice.try_into().unwrap()
|
||||
}
|
||||
|
||||
/// Borrows the data mutably
|
||||
pub fn secret_mut(&mut self) -> &mut [u8; N] {
|
||||
// the same safety argument as for `secret()` holds
|
||||
let ptr = self.ptr as *mut u8;
|
||||
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, N) };
|
||||
slice.try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// The Debug implementation of [Secret] does not reveal the secret data,
|
||||
/// instead a placeholder `<SECRET>` is used
|
||||
impl<const N: usize> fmt::Debug for Secret<N> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str("<SECRET>")
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains information in the form of a byte array that may be known to the
|
||||
/// public
|
||||
// TODO: We should get rid of the Public type; just use a normal value
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct Public<const N: usize> {
|
||||
pub value: [u8; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> Public<N> {
|
||||
/// Create a new [Public] from a byte slice
|
||||
pub fn from_slice(value: &[u8]) -> Self {
|
||||
mutating(Self::zero(), |r| cpy(value, &mut r.value))
|
||||
}
|
||||
|
||||
/// Create a new [Public] from a byte array
|
||||
pub fn new(value: [u8; N]) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
/// Create a zero initialized [Public]
|
||||
pub fn zero() -> Self {
|
||||
Self { value: [0u8; N] }
|
||||
}
|
||||
|
||||
/// Create a random initialized [Public]
|
||||
pub fn random() -> Self {
|
||||
mutating(Self::zero(), |r| r.randomize())
|
||||
}
|
||||
|
||||
/// Randomize all bytes in an existing [Public]
|
||||
pub fn randomize(&mut self) {
|
||||
rosenpass_sodium::helpers::randombytes_buf(&mut self.value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the contents of an `&[u8]` as hexadecimal symbols to a [std::fmt::Formatter]
|
||||
pub fn debug_crypto_array(v: &[u8], fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str("[{}]=")?;
|
||||
if v.len() > 64 {
|
||||
for byte in &v[..32] {
|
||||
std::fmt::LowerHex::fmt(byte, fmt)?;
|
||||
}
|
||||
fmt.write_str("…")?;
|
||||
for byte in &v[v.len() - 32..] {
|
||||
std::fmt::LowerHex::fmt(byte, fmt)?;
|
||||
}
|
||||
} else {
|
||||
for byte in v {
|
||||
std::fmt::LowerHex::fmt(byte, fmt)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for Public<N> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
debug_crypto_array(&self.value, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for Public<N> {
|
||||
type Target = [u8; N];
|
||||
|
||||
fn deref(&self) -> &[u8; N] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DerefMut for Public<N> {
|
||||
fn deref_mut(&mut self) -> &mut [u8; N] {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
/// https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations
|
||||
/// promises us that allocated memory is initialized with this magic byte
|
||||
const SODIUM_MAGIC_BYTE: u8 = 0xdb;
|
||||
|
||||
/// must be called before any interaction with libsodium
|
||||
fn init() {
|
||||
unsafe { libsodium_sys::sodium_init() };
|
||||
}
|
||||
|
||||
/// checks that whe can malloc with libsodium
|
||||
#[test]
|
||||
fn sodium_malloc() {
|
||||
init();
|
||||
const N: usize = 8;
|
||||
let ptr = unsafe { libsodium_sys::sodium_malloc(N) };
|
||||
let mem = unsafe { std::slice::from_raw_parts(ptr as *mut u8, N) };
|
||||
assert_eq!(mem, &[SODIUM_MAGIC_BYTE; N])
|
||||
}
|
||||
|
||||
/// checks that whe can free with libsodium
|
||||
#[test]
|
||||
fn sodium_free() {
|
||||
init();
|
||||
const N: usize = 8;
|
||||
let ptr = unsafe { libsodium_sys::sodium_malloc(N) };
|
||||
unsafe { libsodium_sys::sodium_free(ptr) }
|
||||
}
|
||||
|
||||
/// check that we can alloc using the magic pool
|
||||
#[test]
|
||||
fn secret_memory_pool_take() {
|
||||
init();
|
||||
const N: usize = 0x100;
|
||||
let mut pool = SecretMemoryPool::new();
|
||||
let secret: Secret<N> = pool.take();
|
||||
assert_eq!(secret.secret(), &[0; N]);
|
||||
}
|
||||
|
||||
/// check that a secrete lives, even if its [SecretMemoryPool] is deleted
|
||||
#[test]
|
||||
fn secret_memory_pool_drop() {
|
||||
init();
|
||||
const N: usize = 0x100;
|
||||
let mut pool = SecretMemoryPool::new();
|
||||
let secret: Secret<N> = pool.take();
|
||||
std::mem::drop(pool);
|
||||
assert_eq!(secret.secret(), &[0; N]);
|
||||
}
|
||||
|
||||
/// check that a secrete can be reborn, freshly initialized with zero
|
||||
#[test]
|
||||
fn secret_memory_pool_release() {
|
||||
init();
|
||||
const N: usize = 1;
|
||||
let mut pool = SecretMemoryPool::new();
|
||||
let mut secret: Secret<N> = pool.take();
|
||||
let old_secret_ptr = secret.ptr;
|
||||
|
||||
secret.secret_mut()[0] = 0x13;
|
||||
pool.release(secret);
|
||||
|
||||
// now check that we get the same ptr
|
||||
let new_secret: Secret<N> = pool.take();
|
||||
assert_eq!(old_secret_ptr, new_secret.ptr);
|
||||
|
||||
// and that the secret was zeroized
|
||||
assert_eq!(new_secret.secret(), &[0; N]);
|
||||
}
|
||||
}
|
||||
|
||||
trait StoreSecret {
|
||||
type Error;
|
||||
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
impl<T: StoreValue> StoreSecret for T {
|
||||
type Error = <T as StoreValue>::Error;
|
||||
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::Error> {
|
||||
self.store(path)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> LoadValue for Secret<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
||||
let mut v = Self::random();
|
||||
let p = path.as_ref();
|
||||
fopen_r(p)?
|
||||
.read_exact_to_end(v.secret_mut())
|
||||
.with_context(|| format!("Could not load file {p:?}"))?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> LoadValueB64 for Secret<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn load_b64<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
||||
use std::io::Read;
|
||||
|
||||
let mut v = Self::random();
|
||||
let p = path.as_ref();
|
||||
// This might leave some fragments of the secret on the stack;
|
||||
// in practice this is likely not a problem because the stack likely
|
||||
// will be overwritten by something else soon but this is not exactly
|
||||
// guaranteed. It would be possible to remedy this, but since the secret
|
||||
// data will linger in the Linux page cache anyways with the current
|
||||
// implementation, going to great length to erase the secret here is
|
||||
// not worth it right now.
|
||||
b64_reader(&mut fopen_r(p)?)
|
||||
.read_exact(v.secret_mut())
|
||||
.with_context(|| format!("Could not load base64 file {p:?}"))?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> StoreSecret for Secret<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||
std::fs::write(path, self.secret())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> LoadValue for Public<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
||||
let mut v = Self::random();
|
||||
fopen_r(path)?.read_exact_to_end(&mut *v)?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> StoreValue for Public<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn store<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||
std::fs::write(path, **self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -41,10 +41,6 @@ pub struct RosenpassPeer {
|
||||
#[serde(default)]
|
||||
pub key_out: Option<PathBuf>,
|
||||
|
||||
// TODO make sure failure does not crash but is logged
|
||||
#[serde(default)]
|
||||
pub exchange_command: Vec<String>,
|
||||
|
||||
// TODO make this field only available on binary builds, not on library builds
|
||||
#[serde(flatten)]
|
||||
pub wg: Option<WireGuard>,
|
||||
@@ -345,28 +341,20 @@ impl Rosenpass {
|
||||
/// Generate an example configuration
|
||||
pub fn example_config() -> Self {
|
||||
let peer = RosenpassPeer {
|
||||
public_key: "rp-peer-public-key".into(),
|
||||
public_key: "/path/to/rp-peer-public-key".into(),
|
||||
endpoint: Some("my-peer.test:9999".into()),
|
||||
exchange_command: [
|
||||
"wg",
|
||||
"set",
|
||||
"wg0",
|
||||
"peer",
|
||||
"<PEER_ID>",
|
||||
"preshared-key",
|
||||
"/dev/stdin",
|
||||
]
|
||||
.into_iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect(),
|
||||
key_out: Some("rp-key-out".into()),
|
||||
pre_shared_key: None,
|
||||
wg: None,
|
||||
key_out: Some("/path/to/rp-key-out.txt".into()),
|
||||
pre_shared_key: Some("additional pre shared key".into()),
|
||||
wg: Some(WireGuard {
|
||||
device: "wirgeguard device e.g. wg0".into(),
|
||||
peer: "wireguard public key".into(),
|
||||
extra_params: vec!["passed to".into(), "wg set".into()],
|
||||
}),
|
||||
};
|
||||
|
||||
Self {
|
||||
public_key: "rp-public-key".into(),
|
||||
secret_key: "rp-secret-key".into(),
|
||||
public_key: "/path/to/rp-public-key".into(),
|
||||
secret_key: "/path/to/rp-secret-key".into(),
|
||||
peers: vec![peer],
|
||||
..Self::new("", "")
|
||||
}
|
||||
|
||||
46
rosenpass/src/hash_domains.rs
Normal file
46
rosenpass/src/hash_domains.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
//! Pseudo Random Functions (PRFs) with a tree-like label scheme which
|
||||
//! ensures their uniqueness
|
||||
|
||||
use anyhow::Result;
|
||||
use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN};
|
||||
|
||||
// TODO Use labels that can serve as identifiers
|
||||
macro_rules! hash_domain_ns {
|
||||
($base:ident, $name:ident, $($lbl:expr),* ) => {
|
||||
pub fn $name() -> Result<HashDomain> {
|
||||
let t = $base()?;
|
||||
$( let t = t.mix($lbl.as_bytes())?; )*
|
||||
Ok(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! hash_domain {
|
||||
($base:ident, $name:ident, $($lbl:expr),* ) => {
|
||||
pub fn $name() -> Result<[u8; KEY_LEN]> {
|
||||
let t = $base()?;
|
||||
$( let t = t.mix($lbl.as_bytes())?; )*
|
||||
Ok(t.into_value())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn protocol() -> Result<HashDomain> {
|
||||
HashDomain::zero().mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes())
|
||||
}
|
||||
|
||||
hash_domain_ns!(protocol, mac, "mac");
|
||||
hash_domain_ns!(protocol, cookie, "cookie");
|
||||
hash_domain_ns!(protocol, peerid, "peer id");
|
||||
hash_domain_ns!(protocol, biscuit_ad, "biscuit additional data");
|
||||
hash_domain_ns!(protocol, ckinit, "chaining key init");
|
||||
hash_domain_ns!(protocol, _ckextract, "chaining key extract");
|
||||
|
||||
hash_domain!(_ckextract, mix, "mix");
|
||||
hash_domain!(_ckextract, hs_enc, "handshake encryption");
|
||||
hash_domain!(_ckextract, ini_enc, "initiator handshake encryption");
|
||||
hash_domain!(_ckextract, res_enc, "responder handshake encryption");
|
||||
|
||||
hash_domain_ns!(_ckextract, _user, "user");
|
||||
hash_domain_ns!(_user, _rp, "rosenpass.eu");
|
||||
hash_domain!(_rp, osk, "wireguard psk");
|
||||
@@ -1,48 +0,0 @@
|
||||
//! Pseudo Random Functions (PRFs) with a tree-like label scheme which
|
||||
//! ensures their uniqueness
|
||||
|
||||
|
||||
use crate::prftree::PrfTree;
|
||||
use anyhow::Result;
|
||||
use rosenpass_ciphers::KEY_LEN;
|
||||
|
||||
pub fn protocol() -> Result<PrfTree> {
|
||||
PrfTree::zero().mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 BLAKE2s".as_bytes())
|
||||
}
|
||||
|
||||
// TODO Use labels that can serve as identifiers
|
||||
macro_rules! prflabel {
|
||||
($base:ident, $name:ident, $($lbl:expr),* ) => {
|
||||
pub fn $name() -> Result<PrfTree> {
|
||||
let t = $base()?;
|
||||
$( let t = t.mix($lbl.as_bytes())?; )*
|
||||
Ok(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prflabel!(protocol, mac, "mac");
|
||||
prflabel!(protocol, cookie, "cookie");
|
||||
prflabel!(protocol, peerid, "peer id");
|
||||
prflabel!(protocol, biscuit_ad, "biscuit additional data");
|
||||
prflabel!(protocol, ckinit, "chaining key init");
|
||||
prflabel!(protocol, _ckextract, "chaining key extract");
|
||||
|
||||
macro_rules! prflabel_leaf {
|
||||
($base:ident, $name:ident, $($lbl:expr),* ) => {
|
||||
pub fn $name() -> Result<[u8; KEY_LEN]> {
|
||||
let t = $base()?;
|
||||
$( let t = t.mix($lbl.as_bytes())?; )*
|
||||
Ok(t.into_value())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prflabel_leaf!(_ckextract, mix, "mix");
|
||||
prflabel_leaf!(_ckextract, hs_enc, "handshake encryption");
|
||||
prflabel_leaf!(_ckextract, ini_enc, "initiator handshake encryption");
|
||||
prflabel_leaf!(_ckextract, res_enc, "responder handshake encryption");
|
||||
|
||||
prflabel!(_ckextract, _user, "user");
|
||||
prflabel!(_user, _rp, "rosenpass.eu");
|
||||
prflabel_leaf!(_rp, osk, "wireguard psk");
|
||||
@@ -1,56 +1,24 @@
|
||||
pub mod coloring;
|
||||
#[rustfmt::skip]
|
||||
pub mod labeled_prf;
|
||||
use rosenpass_lenses::LenseError;
|
||||
|
||||
pub mod app_server;
|
||||
pub mod cli;
|
||||
pub mod config;
|
||||
pub mod hash_domains;
|
||||
pub mod msgs;
|
||||
pub mod pqkem;
|
||||
pub mod prftree;
|
||||
pub mod protocol;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum RosenpassError {
|
||||
#[error("error in OQS")]
|
||||
Oqs,
|
||||
#[error("error from external library while calling OQS")]
|
||||
OqsExternalLib,
|
||||
#[error("buffer size mismatch, required {required_size} but found {actual_size}")]
|
||||
BufferSizeMismatch {
|
||||
required_size: usize,
|
||||
actual_size: usize,
|
||||
},
|
||||
#[error("buffer size mismatch")]
|
||||
BufferSizeMismatch,
|
||||
#[error("invalid message type")]
|
||||
InvalidMessageType(u8),
|
||||
}
|
||||
|
||||
impl RosenpassError {
|
||||
/// Helper function to check a buffer size
|
||||
fn check_buffer_size(required_size: usize, actual_size: usize) -> Result<(), Self> {
|
||||
if required_size != actual_size {
|
||||
Err(Self::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait to attach function calls to foreign types.
|
||||
trait RosenpassMaybeError {
|
||||
/// Checks whether something is an error or not
|
||||
fn to_rg_error(&self) -> Result<(), RosenpassError>;
|
||||
}
|
||||
|
||||
impl RosenpassMaybeError for oqs_sys::common::OQS_STATUS {
|
||||
fn to_rg_error(&self) -> Result<(), RosenpassError> {
|
||||
use oqs_sys::common::OQS_STATUS;
|
||||
match self {
|
||||
OQS_STATUS::OQS_SUCCESS => Ok(()),
|
||||
OQS_STATUS::OQS_ERROR => Err(RosenpassError::Oqs),
|
||||
OQS_STATUS::OQS_EXTERNAL_LIB_ERROR_OPENSSL => Err(RosenpassError::OqsExternalLib),
|
||||
impl From<LenseError> for RosenpassError {
|
||||
fn from(value: LenseError) -> Self {
|
||||
match value {
|
||||
LenseError::BufferSizeMismatch => RosenpassError::BufferSizeMismatch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ use std::process::exit;
|
||||
|
||||
/// Catches errors, prints them through the logger, then exits
|
||||
pub fn main() {
|
||||
env_logger::init();
|
||||
// default to displaying warning and error log messages only
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
||||
|
||||
let res = attempt!({
|
||||
rosenpass_sodium::init()?;
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! The following example uses the [`data_lense` macro](crate::data_lense) to create a lense that
|
||||
//! The following example uses the [`lense` macro](rosenpass_lenses::lense) to create a lense that
|
||||
//! might be useful when dealing with UDP headers.
|
||||
//!
|
||||
//! ```
|
||||
//! use rosenpass::{data_lense, RosenpassError, msgs::LenseView};
|
||||
//! use rosenpass_lenses::{lense, LenseView};
|
||||
//! use rosenpass::RosenpassError;
|
||||
//! # fn main() -> Result<(), RosenpassError> {
|
||||
//!
|
||||
//! data_lense! {UdpDatagramHeader :=
|
||||
//! lense! {UdpDatagramHeader :=
|
||||
//! source_port: 2,
|
||||
//! dest_port: 2,
|
||||
//! length: 2,
|
||||
@@ -44,219 +45,14 @@
|
||||
//! ```
|
||||
|
||||
use super::RosenpassError;
|
||||
use crate::pqkem::*;
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
|
||||
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
||||
use rosenpass_lenses::{lense, LenseView};
|
||||
|
||||
// Macro magic ////////////////////////////////////////////////////////////////
|
||||
|
||||
/// A macro to create data lenses. Refer to the [`msgs` mod](crate::msgs) for
|
||||
/// an example and further elaboration
|
||||
// TODO implement TryFrom<[u8]> and From<[u8; Self::len()]>
|
||||
#[macro_export]
|
||||
macro_rules! data_lense(
|
||||
// prefix @ offset ; optional meta ; field name : field length, ...
|
||||
(token_muncher_ref @ $offset:expr ; $( $attr:meta )* ; $field:ident : $len:expr $(, $( $tail:tt )+ )?) => {
|
||||
::paste::paste!{
|
||||
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
$( #[ $attr ] )*
|
||||
///
|
||||
#[doc = data_lense!(maybe_docstring_link $len)]
|
||||
/// bytes long
|
||||
pub fn $field(&self) -> &__ContainerType::Output {
|
||||
&self.0[$offset .. $offset + $len]
|
||||
}
|
||||
|
||||
/// The bytes until the
|
||||
#[doc = data_lense!(maybe_docstring_link Self::$field)]
|
||||
/// field
|
||||
pub fn [< until_ $field >](&self) -> &__ContainerType::Output {
|
||||
&self.0[0 .. $offset]
|
||||
}
|
||||
|
||||
// if the tail exits, consume it as well
|
||||
$(
|
||||
data_lense!{token_muncher_ref @ $offset + $len ; $( $tail )+ }
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
// prefix @ offset ; optional meta ; field name : field length, ...
|
||||
(token_muncher_mut @ $offset:expr ; $( $attr:meta )* ; $field:ident : $len:expr $(, $( $tail:tt )+ )?) => {
|
||||
::paste::paste!{
|
||||
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
$( #[ $attr ] )*
|
||||
///
|
||||
#[doc = data_lense!(maybe_docstring_link $len)]
|
||||
/// bytes long
|
||||
pub fn [< $field _mut >](&mut self) -> &mut __ContainerType::Output {
|
||||
&mut self.0[$offset .. $offset + $len]
|
||||
}
|
||||
|
||||
// if the tail exits, consume it as well
|
||||
$(
|
||||
data_lense!{token_muncher_mut @ $offset + $len ; $( $tail )+ }
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
// switch that yields literals unchanged, but creates docstring links to
|
||||
// constants
|
||||
// TODO the doc string link doesn't work if $x is taken from a generic,
|
||||
(maybe_docstring_link $x:literal) => (stringify!($x));
|
||||
(maybe_docstring_link $x:expr) => (stringify!([$x]));
|
||||
|
||||
// struct name < optional generics > := optional doc string field name : field length, ...
|
||||
($type:ident $( < $( $generic:ident ),+ > )? := $( $( #[ $attr:meta ] )* $field:ident : $len:expr ),+) => (::paste::paste!{
|
||||
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
/// A data lense to manipulate byte slices.
|
||||
///
|
||||
//// # Fields
|
||||
///
|
||||
$(
|
||||
/// - `
|
||||
#[doc = stringify!($field)]
|
||||
/// `:
|
||||
#[doc = data_lense!(maybe_docstring_link $len)]
|
||||
/// bytes
|
||||
)+
|
||||
pub struct $type<__ContainerType $(, $( $generic ),+ )? > (
|
||||
__ContainerType,
|
||||
// The phantom data is required, since all generics declared on a
|
||||
// type need to be used on the type.
|
||||
// https://doc.rust-lang.org/stable/error_codes/E0392.html
|
||||
$( $( ::core::marker::PhantomData<$generic> ),+ )?
|
||||
);
|
||||
|
||||
impl<__ContainerType $(, $( $generic: LenseView ),+ )? > $type<__ContainerType $(, $( $generic ),+ )? >{
|
||||
$(
|
||||
/// Size in bytes of the field `
|
||||
#[doc = !($field)]
|
||||
/// `
|
||||
pub const fn [< $field _len >]() -> usize{
|
||||
$len
|
||||
}
|
||||
)+
|
||||
|
||||
/// Verify that `len` is sufficiently long to hold [Self]
|
||||
pub fn check_size(len: usize) -> Result<(), RosenpassError>{
|
||||
let required_size = $( $len + )+ 0;
|
||||
let actual_size = len;
|
||||
if required_size != actual_size {
|
||||
Err(RosenpassError::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
})
|
||||
}else{
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// read-only accessor functions
|
||||
impl<'a, __ContainerType $(, $( $generic: LenseView ),+ )?> $type<&'a __ContainerType $(, $( $generic ),+ )?>
|
||||
where
|
||||
__ContainerType: std::ops::Index<std::ops::Range<usize>> + ?Sized,
|
||||
{
|
||||
data_lense!{token_muncher_ref @ 0 ; $( $( $attr )* ; $field : $len ),+ }
|
||||
|
||||
/// View into all bytes belonging to this Lense
|
||||
pub fn all_bytes(&self) -> &__ContainerType::Output {
|
||||
&self.0[0..Self::LEN]
|
||||
}
|
||||
}
|
||||
|
||||
// mutable accessor functions
|
||||
impl<'a, __ContainerType $(, $( $generic: LenseView ),+ )?> $type<&'a mut __ContainerType $(, $( $generic ),+ )?>
|
||||
where
|
||||
__ContainerType: std::ops::IndexMut<std::ops::Range<usize>> + ?Sized,
|
||||
{
|
||||
data_lense!{token_muncher_ref @ 0 ; $( $( $attr )* ; $field : $len ),+ }
|
||||
data_lense!{token_muncher_mut @ 0 ; $( $( $attr )* ; $field : $len ),+ }
|
||||
|
||||
/// View into all bytes belonging to this Lense
|
||||
pub fn all_bytes(&self) -> &__ContainerType::Output {
|
||||
&self.0[0..Self::LEN]
|
||||
}
|
||||
|
||||
/// View into all bytes belonging to this Lense
|
||||
pub fn all_bytes_mut(&mut self) -> &mut __ContainerType::Output {
|
||||
&mut self.0[0..Self::LEN]
|
||||
}
|
||||
}
|
||||
|
||||
// lense trait, allowing us to know the implementing lenses size
|
||||
impl<__ContainerType $(, $( $generic: LenseView ),+ )? > LenseView for $type<__ContainerType $(, $( $generic ),+ )? >{
|
||||
/// Number of bytes required to store this type in binary format
|
||||
const LEN: usize = $( $len + )+ 0;
|
||||
}
|
||||
|
||||
/// Extension trait to allow checked creation of a lense over
|
||||
/// some byte slice that contains a
|
||||
#[doc = data_lense!(maybe_docstring_link $type)]
|
||||
pub trait [< $type Ext >] {
|
||||
type __ContainerType;
|
||||
|
||||
/// Create a lense to the byte slice
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError>;
|
||||
|
||||
/// Create a lense to the byte slice, automatically truncating oversized buffers
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError>;
|
||||
}
|
||||
|
||||
impl<'a> [< $type Ext >] for &'a [u8] {
|
||||
type __ContainerType = &'a [u8];
|
||||
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
$type::<Self::__ContainerType, $( $($generic),+ )? >::check_size(self.len())?;
|
||||
Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? ))
|
||||
}
|
||||
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
let required_size = $( $len + )+ 0;
|
||||
let actual_size = self.len();
|
||||
if actual_size < required_size {
|
||||
return Err(RosenpassError::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
});
|
||||
}
|
||||
|
||||
[< $type Ext >]::[< $type:snake >](&self[..required_size])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> [< $type Ext >] for &'a mut [u8] {
|
||||
type __ContainerType = &'a mut [u8];
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
$type::<Self::__ContainerType, $( $($generic),+ )? >::check_size(self.len())?;
|
||||
Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? ))
|
||||
}
|
||||
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
let required_size = $( $len + )+ 0;
|
||||
let actual_size = self.len();
|
||||
if actual_size < required_size {
|
||||
return Err(RosenpassError::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
});
|
||||
}
|
||||
|
||||
[< $type Ext >]::[< $type:snake >](&mut self[..required_size])
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
/// Common trait shared by all Lenses
|
||||
pub trait LenseView {
|
||||
const LEN: usize;
|
||||
}
|
||||
|
||||
data_lense! { Envelope<M> :=
|
||||
lense! { Envelope<M> :=
|
||||
/// [MsgType] of this message
|
||||
msg_type: 1,
|
||||
/// Reserved for future use
|
||||
@@ -270,35 +66,35 @@ data_lense! { Envelope<M> :=
|
||||
cookie: 16
|
||||
}
|
||||
|
||||
data_lense! { InitHello :=
|
||||
lense! { InitHello :=
|
||||
/// Randomly generated connection id
|
||||
sidi: 4,
|
||||
/// Kyber 512 Ephemeral Public Key
|
||||
epki: EphemeralKEM::PK_LEN,
|
||||
epki: EphemeralKem::PK_LEN,
|
||||
/// Classic McEliece Ciphertext
|
||||
sctr: StaticKEM::CT_LEN,
|
||||
sctr: StaticKem::CT_LEN,
|
||||
/// Encryped: 16 byte hash of McEliece initiator static key
|
||||
pidic: aead::TAG_LEN + 32,
|
||||
/// Encrypted TAI64N Time Stamp (against replay attacks)
|
||||
auth: aead::TAG_LEN
|
||||
}
|
||||
|
||||
data_lense! { RespHello :=
|
||||
lense! { RespHello :=
|
||||
/// Randomly generated connection id
|
||||
sidr: 4,
|
||||
/// Copied from InitHello
|
||||
sidi: 4,
|
||||
/// Kyber 512 Ephemeral Ciphertext
|
||||
ecti: EphemeralKEM::CT_LEN,
|
||||
ecti: EphemeralKem::CT_LEN,
|
||||
/// Classic McEliece Ciphertext
|
||||
scti: StaticKEM::CT_LEN,
|
||||
scti: StaticKem::CT_LEN,
|
||||
/// Empty encrypted message (just an auth tag)
|
||||
auth: aead::TAG_LEN,
|
||||
/// Responders handshake state in encrypted form
|
||||
biscuit: BISCUIT_CT_LEN
|
||||
}
|
||||
|
||||
data_lense! { InitConf :=
|
||||
lense! { InitConf :=
|
||||
/// Copied from InitHello
|
||||
sidi: 4,
|
||||
/// Copied from RespHello
|
||||
@@ -309,7 +105,7 @@ data_lense! { InitConf :=
|
||||
auth: aead::TAG_LEN
|
||||
}
|
||||
|
||||
data_lense! { EmptyData :=
|
||||
lense! { EmptyData :=
|
||||
/// Copied from RespHello
|
||||
sid: 4,
|
||||
/// Nonce
|
||||
@@ -318,7 +114,7 @@ data_lense! { EmptyData :=
|
||||
auth: aead::TAG_LEN
|
||||
}
|
||||
|
||||
data_lense! { Biscuit :=
|
||||
lense! { Biscuit :=
|
||||
/// H(spki) – Ident ifies the initiator
|
||||
pidi: KEY_LEN,
|
||||
/// The biscuit number (replay protection)
|
||||
@@ -327,11 +123,11 @@ data_lense! { Biscuit :=
|
||||
ck: KEY_LEN
|
||||
}
|
||||
|
||||
data_lense! { DataMsg :=
|
||||
lense! { DataMsg :=
|
||||
dummy: 4
|
||||
}
|
||||
|
||||
data_lense! { CookieReply :=
|
||||
lense! { CookieReply :=
|
||||
dummy: 4
|
||||
}
|
||||
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
//! Traits and implementations for Key Encapsulation Mechanisms (KEMs)
|
||||
//!
|
||||
//! KEMs are the interface provided by almost all post-quantum
|
||||
//! secure key exchange mechanisms.
|
||||
//!
|
||||
//! Conceptually KEMs are akin to public-key encryption, but instead of encrypting
|
||||
//! arbitrary data, KEMs are limited to the transmission of keys, randomly chosen during
|
||||
//!
|
||||
//! encapsulation.
|
||||
//! The [KEM] Trait describes the basic API offered by a Key Encapsulation
|
||||
//! Mechanism. Two implementations for it are provided, [StaticKEM] and [EphemeralKEM].
|
||||
|
||||
use crate::{RosenpassError, RosenpassMaybeError};
|
||||
|
||||
/// Key Encapsulation Mechanism
|
||||
///
|
||||
/// The KEM interface defines three operations: Key generation, key encapsulation and key
|
||||
/// decapsulation.
|
||||
pub trait KEM {
|
||||
/// Secrete Key length
|
||||
const SK_LEN: usize;
|
||||
/// Public Key length
|
||||
const PK_LEN: usize;
|
||||
/// Ciphertext length
|
||||
const CT_LEN: usize;
|
||||
/// Shared Secret length
|
||||
const SHK_LEN: usize;
|
||||
|
||||
/// Generate a keypair consisting of secret key (`sk`) and public key (`pk`)
|
||||
///
|
||||
/// `keygen() -> sk, pk`
|
||||
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError>;
|
||||
|
||||
/// From a public key (`pk`), generate a shared key (`shk`, for local use)
|
||||
/// and a cipher text (`ct`, to be sent to the owner of the `pk`).
|
||||
///
|
||||
/// `encaps(pk) -> shk, ct`
|
||||
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError>;
|
||||
|
||||
/// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key
|
||||
/// (`shk`)
|
||||
///
|
||||
/// `decaps(sk, ct) -> shk`
|
||||
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError>;
|
||||
}
|
||||
|
||||
/// A KEM that is secure against Chosen Ciphertext Attacks (CCA).
|
||||
/// In the context of rosenpass this is used for static keys.
|
||||
/// Uses [Classic McEliece](https://classic.mceliece.org/) 460896 from liboqs.
|
||||
///
|
||||
/// Classic McEliece is chosen because of its high security margin and its small
|
||||
/// ciphertexts. The public keys are humongous, but (being static keys) the are never transmitted over
|
||||
/// the wire so this is not a big problem.
|
||||
pub struct StaticKEM;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte
|
||||
/// slices only identified using raw pointers. It must be ensured that the raw
|
||||
/// pointers point into byte slices of sufficient length, to avoid UB through
|
||||
/// overwriting of arbitrary data. This is checked in the following code before
|
||||
/// the unsafe calls, and an early return with an Err occurs if the byte slice
|
||||
/// size does not match the required size.
|
||||
///
|
||||
/// __Note__: This requirement is stricter than necessary, it would suffice
|
||||
/// to only check that the buffers are big enough, allowing them to be even
|
||||
/// bigger. However, from a correctness point of view it does not make sense to
|
||||
/// allow bigger buffers.
|
||||
impl KEM for StaticKEM {
|
||||
const SK_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_secret_key as usize;
|
||||
const PK_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_public_key as usize;
|
||||
const CT_LEN: usize = oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_ciphertext as usize;
|
||||
const SHK_LEN: usize =
|
||||
oqs_sys::kem::OQS_KEM_classic_mceliece_460896_length_shared_secret as usize;
|
||||
|
||||
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError> {
|
||||
RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?;
|
||||
RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?;
|
||||
unsafe {
|
||||
oqs_sys::kem::OQS_KEM_classic_mceliece_460896_keypair(pk.as_mut_ptr(), sk.as_mut_ptr())
|
||||
.to_rg_error()
|
||||
}
|
||||
}
|
||||
|
||||
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError> {
|
||||
RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?;
|
||||
RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?;
|
||||
RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?;
|
||||
unsafe {
|
||||
oqs_sys::kem::OQS_KEM_classic_mceliece_460896_encaps(
|
||||
ct.as_mut_ptr(),
|
||||
shk.as_mut_ptr(),
|
||||
pk.as_ptr(),
|
||||
)
|
||||
.to_rg_error()
|
||||
}
|
||||
}
|
||||
|
||||
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError> {
|
||||
RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?;
|
||||
RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?;
|
||||
RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?;
|
||||
unsafe {
|
||||
oqs_sys::kem::OQS_KEM_classic_mceliece_460896_decaps(
|
||||
shk.as_mut_ptr(),
|
||||
ct.as_ptr(),
|
||||
sk.as_ptr(),
|
||||
)
|
||||
.to_rg_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements a KEM that is secure against Chosen Plaintext Attacks (CPA).
|
||||
/// In the context of rosenpass this is used for ephemeral keys.
|
||||
/// Currently the implementation uses
|
||||
/// [Kyber 512](https://openquantumsafe.org/liboqs/algorithms/kem/kyber) from liboqs.
|
||||
///
|
||||
/// This is being used for ephemeral keys; since these are use-once the first post quantum
|
||||
/// wireguard paper claimed that CPA security would be sufficient. Nonetheless we choose kyber
|
||||
/// which provides CCA security since there are no publicly vetted KEMs out there which provide
|
||||
/// only CPA security.
|
||||
pub struct EphemeralKEM;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This Trait impl calls unsafe [oqs_sys] functions, that write to byte
|
||||
/// slices only identified using raw pointers. It must be ensured that the raw
|
||||
/// pointers point into byte slices of sufficient length, to avoid UB through
|
||||
/// overwriting of arbitrary data. This is checked in the following code before
|
||||
/// the unsafe calls, and an early return with an Err occurs if the byte slice
|
||||
/// size does not match the required size.
|
||||
///
|
||||
/// __Note__: This requirement is stricter than necessary, it would suffice
|
||||
/// to only check that the buffers are big enough, allowing them to be even
|
||||
/// bigger. However, from a correctness point of view it does not make sense to
|
||||
/// allow bigger buffers.
|
||||
impl KEM for EphemeralKEM {
|
||||
const SK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_secret_key as usize;
|
||||
const PK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_public_key as usize;
|
||||
const CT_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_ciphertext as usize;
|
||||
const SHK_LEN: usize = oqs_sys::kem::OQS_KEM_kyber_512_length_shared_secret as usize;
|
||||
fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), RosenpassError> {
|
||||
RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?;
|
||||
RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?;
|
||||
unsafe {
|
||||
oqs_sys::kem::OQS_KEM_kyber_512_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()).to_rg_error()
|
||||
}
|
||||
}
|
||||
fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), RosenpassError> {
|
||||
RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?;
|
||||
RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?;
|
||||
RosenpassError::check_buffer_size(pk.len(), Self::PK_LEN)?;
|
||||
unsafe {
|
||||
oqs_sys::kem::OQS_KEM_kyber_512_encaps(ct.as_mut_ptr(), shk.as_mut_ptr(), pk.as_ptr())
|
||||
.to_rg_error()
|
||||
}
|
||||
}
|
||||
fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), RosenpassError> {
|
||||
RosenpassError::check_buffer_size(shk.len(), Self::SHK_LEN)?;
|
||||
RosenpassError::check_buffer_size(sk.len(), Self::SK_LEN)?;
|
||||
RosenpassError::check_buffer_size(ct.len(), Self::CT_LEN)?;
|
||||
unsafe {
|
||||
oqs_sys::kem::OQS_KEM_kyber_512_decaps(shk.as_mut_ptr(), ct.as_ptr(), sk.as_ptr())
|
||||
.to_rg_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
//! Implementation of the tree-like structure used for the label derivation in [labeled_prf](crate::labeled_prf)
|
||||
use crate::coloring::Secret;
|
||||
|
||||
use anyhow::Result;
|
||||
use rosenpass_ciphers::{hash, KEY_LEN};
|
||||
use rosenpass_to::To;
|
||||
|
||||
// TODO Use a proper Dec interface
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PrfTree([u8; KEY_LEN]);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PrfTreeBranch([u8; KEY_LEN]);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretPrfTree(Secret<KEY_LEN>);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SecretPrfTreeBranch(Secret<KEY_LEN>);
|
||||
|
||||
impl PrfTree {
|
||||
pub fn zero() -> Self {
|
||||
Self([0u8; KEY_LEN])
|
||||
}
|
||||
|
||||
pub fn dup(self) -> PrfTreeBranch {
|
||||
PrfTreeBranch(self.0)
|
||||
}
|
||||
|
||||
pub fn into_secret_prf_tree(self) -> SecretPrfTree {
|
||||
SecretPrfTree(Secret::from_slice(&self.0))
|
||||
}
|
||||
|
||||
// TODO: Protocol! Use domain separation to ensure that
|
||||
pub fn mix(self, v: &[u8]) -> Result<Self> {
|
||||
Ok(Self(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?))
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretPrfTree> {
|
||||
SecretPrfTree::prf_invoc(&self.0, v.secret())
|
||||
}
|
||||
|
||||
pub fn into_value(self) -> [u8; KEY_LEN] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PrfTreeBranch {
|
||||
pub fn mix(&self, v: &[u8]) -> Result<PrfTree> {
|
||||
Ok(PrfTree(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?))
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretPrfTree> {
|
||||
SecretPrfTree::prf_invoc(&self.0, v.secret())
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretPrfTree {
|
||||
pub fn prf_invoc(k: &[u8], d: &[u8]) -> Result<SecretPrfTree> {
|
||||
let mut r = SecretPrfTree(Secret::zero());
|
||||
hash::hash(k, d).to(r.0.secret_mut())?;
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self(Secret::zero())
|
||||
}
|
||||
|
||||
pub fn dup(self) -> SecretPrfTreeBranch {
|
||||
SecretPrfTreeBranch(self.0)
|
||||
}
|
||||
|
||||
pub fn danger_from_secret(k: Secret<KEY_LEN>) -> Self {
|
||||
Self(k)
|
||||
}
|
||||
|
||||
pub fn mix(self, v: &[u8]) -> Result<SecretPrfTree> {
|
||||
Self::prf_invoc(self.0.secret(), v)
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretPrfTree> {
|
||||
Self::prf_invoc(self.0.secret(), v.secret())
|
||||
}
|
||||
|
||||
pub fn into_secret(self) -> Secret<KEY_LEN> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn into_secret_slice(mut self, v: &[u8], dst: &[u8]) -> Result<()> {
|
||||
hash::hash(v, dst).to(self.0.secret_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl SecretPrfTreeBranch {
|
||||
pub fn mix(&self, v: &[u8]) -> Result<SecretPrfTree> {
|
||||
SecretPrfTree::prf_invoc(self.0.secret(), v)
|
||||
}
|
||||
|
||||
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretPrfTree> {
|
||||
SecretPrfTree::prf_invoc(self.0.secret(), v.secret())
|
||||
}
|
||||
|
||||
// TODO: This entire API is not very nice; we need this for biscuits, but
|
||||
// it might be better to extract a special "biscuit"
|
||||
// labeled subkey and reinitialize the chain with this
|
||||
pub fn danger_into_secret(self) -> Secret<KEY_LEN> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,9 @@
|
||||
//! [CryptoServer].
|
||||
//!
|
||||
//! ```
|
||||
//! use rosenpass_cipher_traits::Kem;
|
||||
//! use rosenpass_ciphers::kem::StaticKem;
|
||||
//! use rosenpass::{
|
||||
//! pqkem::{StaticKEM, KEM},
|
||||
//! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey},
|
||||
//! };
|
||||
//! # fn main() -> anyhow::Result<()> {
|
||||
@@ -30,11 +31,11 @@
|
||||
//!
|
||||
//! // initialize secret and public key for peer a ...
|
||||
//! 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.secret_mut())?;
|
||||
//!
|
||||
//! // ... and for peer b
|
||||
//! 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.secret_mut())?;
|
||||
//!
|
||||
//! // initialize server and a pre-shared key
|
||||
//! let psk = SymKey::random();
|
||||
@@ -67,20 +68,20 @@
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use crate::{
|
||||
coloring::*,
|
||||
labeled_prf as lprf,
|
||||
msgs::*,
|
||||
pqkem::*,
|
||||
prftree::{SecretPrfTree, SecretPrfTreeBranch},
|
||||
};
|
||||
use crate::{hash_domains, msgs::*};
|
||||
use anyhow::{bail, ensure, Context, Result};
|
||||
use rosenpass_cipher_traits::Kem;
|
||||
use rosenpass_ciphers::hash_domain::{SecretHashDomain, SecretHashDomainNamespace};
|
||||
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
|
||||
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
|
||||
use rosenpass_lenses::LenseView;
|
||||
use rosenpass_secret_memory::{Public, Secret};
|
||||
use rosenpass_util::{cat, mem::cpy_min, ord::max_usize, time::Timebase};
|
||||
use std::collections::hash_map::{
|
||||
Entry::{Occupied, Vacant},
|
||||
HashMap,
|
||||
};
|
||||
use std::convert::Infallible;
|
||||
|
||||
// CONSTANTS & SETTINGS //////////////////////////
|
||||
|
||||
@@ -139,10 +140,10 @@ pub fn has_happened(ev: Timing, now: Timing) -> bool {
|
||||
|
||||
// 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 SSk = Secret<{ StaticKEM::SK_LEN }>;
|
||||
pub type EPk = Public<{ EphemeralKEM::PK_LEN }>;
|
||||
pub type ESk = Secret<{ EphemeralKEM::SK_LEN }>;
|
||||
pub type SPk = Secret<{ StaticKem::PK_LEN }>; // Just Secret<> instead of Public<> so it gets allocated on the heap
|
||||
pub type SSk = Secret<{ StaticKem::SK_LEN }>;
|
||||
pub type EPk = Public<{ EphemeralKem::PK_LEN }>;
|
||||
pub type ESk = Secret<{ EphemeralKem::SK_LEN }>;
|
||||
|
||||
pub type SymKey = Secret<KEY_LEN>;
|
||||
pub type SymHash = Public<KEY_LEN>;
|
||||
@@ -233,7 +234,7 @@ pub struct HandshakeState {
|
||||
/// Session ID of Responder
|
||||
pub sidr: SessionId,
|
||||
/// Chaining Key
|
||||
pub ck: SecretPrfTreeBranch,
|
||||
pub ck: SecretHashDomainNamespace, // TODO: We should probably add an abstr
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)]
|
||||
@@ -285,7 +286,7 @@ pub struct Session {
|
||||
pub sidt: SessionId,
|
||||
pub handshake_role: HandshakeRole,
|
||||
// Crypto
|
||||
pub ck: SecretPrfTreeBranch,
|
||||
pub ck: SecretHashDomainNamespace,
|
||||
/// Key for Transmission ("transmission key mine")
|
||||
pub txkm: SymKey,
|
||||
/// Key for Reception ("transmission key theirs")
|
||||
@@ -460,7 +461,7 @@ impl CryptoServer {
|
||||
#[rustfmt::skip]
|
||||
pub fn pidm(&self) -> Result<PeerId> {
|
||||
Ok(Public::new(
|
||||
lprf::peerid()?
|
||||
hash_domains::peerid()?
|
||||
.mix(self.spkm.secret())?
|
||||
.into_value()))
|
||||
}
|
||||
@@ -590,7 +591,7 @@ impl Peer {
|
||||
#[rustfmt::skip]
|
||||
pub fn pidt(&self) -> Result<PeerId> {
|
||||
Ok(Public::new(
|
||||
lprf::peerid()?
|
||||
hash_domains::peerid()?
|
||||
.mix(self.spkt.secret())?
|
||||
.into_value()))
|
||||
}
|
||||
@@ -603,7 +604,7 @@ impl Session {
|
||||
sidm: SessionId::zero(),
|
||||
sidt: SessionId::zero(),
|
||||
handshake_role: HandshakeRole::Initiator,
|
||||
ck: SecretPrfTree::zero().dup(),
|
||||
ck: SecretHashDomain::zero().dup(),
|
||||
txkm: SymKey::zero(),
|
||||
txkt: SymKey::zero(),
|
||||
txnm: 0,
|
||||
@@ -1154,7 +1155,7 @@ impl IniHsPtr {
|
||||
.min(ih.tx_count as f64),
|
||||
)
|
||||
* RETRANSMIT_DELAY_JITTER
|
||||
* (rosenpass_sodium::helpers::rand_f64() + 1.0); // TODO: Replace with the rand crate
|
||||
* (rand::random::<f64>() + 1.0); // TODO: Replace with the rand crate
|
||||
ih.tx_count += 1;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1174,7 +1175,7 @@ where
|
||||
{
|
||||
/// Calculate the message authentication code (`mac`)
|
||||
pub fn seal(&mut self, peer: PeerPtr, srv: &CryptoServer) -> Result<()> {
|
||||
let mac = lprf::mac()?
|
||||
let mac = hash_domains::mac()?
|
||||
.mix(peer.get(srv).spkt.secret())?
|
||||
.mix(self.until_mac())?;
|
||||
self.mac_mut()
|
||||
@@ -1189,7 +1190,9 @@ where
|
||||
{
|
||||
/// Check the message authentication code
|
||||
pub fn check_seal(&self, srv: &CryptoServer) -> Result<bool> {
|
||||
let expected = lprf::mac()?.mix(srv.spkm.secret())?.mix(self.until_mac())?;
|
||||
let expected = hash_domains::mac()?
|
||||
.mix(srv.spkm.secret())?
|
||||
.mix(self.until_mac())?;
|
||||
Ok(rosenpass_sodium::helpers::memcmp(
|
||||
self.mac(),
|
||||
&expected.into_value()[..16],
|
||||
@@ -1219,38 +1222,38 @@ impl HandshakeState {
|
||||
Self {
|
||||
sidi: SessionId::zero(),
|
||||
sidr: SessionId::zero(),
|
||||
ck: SecretPrfTree::zero().dup(),
|
||||
ck: SecretHashDomain::zero().dup(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn erase(&mut self) {
|
||||
self.ck = SecretPrfTree::zero().dup();
|
||||
self.ck = SecretHashDomain::zero().dup();
|
||||
}
|
||||
|
||||
pub fn init(&mut self, spkr: &[u8]) -> Result<&mut Self> {
|
||||
self.ck = lprf::ckinit()?.mix(spkr)?.into_secret_prf_tree().dup();
|
||||
self.ck = hash_domains::ckinit()?.turn_secret().mix(spkr)?.dup();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn mix(&mut self, a: &[u8]) -> Result<&mut Self> {
|
||||
self.ck = self.ck.mix(&lprf::mix()?)?.mix(a)?.dup();
|
||||
self.ck = self.ck.mix(&hash_domains::mix()?)?.mix(a)?.dup();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn encrypt_and_mix(&mut self, ct: &mut [u8], pt: &[u8]) -> Result<&mut Self> {
|
||||
let k = self.ck.mix(&lprf::hs_enc()?)?.into_secret();
|
||||
let k = self.ck.mix(&hash_domains::hs_enc()?)?.into_secret();
|
||||
aead::encrypt(ct, k.secret(), &[0u8; aead::NONCE_LEN], &[], pt)?;
|
||||
self.mix(ct)
|
||||
}
|
||||
|
||||
pub fn decrypt_and_mix(&mut self, pt: &mut [u8], ct: &[u8]) -> Result<&mut Self> {
|
||||
let k = self.ck.mix(&lprf::hs_enc()?)?.into_secret();
|
||||
let k = self.ck.mix(&hash_domains::hs_enc()?)?.into_secret();
|
||||
aead::decrypt(pt, k.secret(), &[0u8; aead::NONCE_LEN], &[], ct)?;
|
||||
self.mix(ct)
|
||||
}
|
||||
|
||||
// I loathe "error: constant expression depends on a generic parameter"
|
||||
pub fn encaps_and_mix<T: KEM, const SHK_LEN: usize>(
|
||||
pub fn encaps_and_mix<T: Kem<Error = Infallible>, const SHK_LEN: usize>(
|
||||
&mut self,
|
||||
ct: &mut [u8],
|
||||
pk: &[u8],
|
||||
@@ -1260,7 +1263,7 @@ impl HandshakeState {
|
||||
self.mix(pk)?.mix(shk.secret())?.mix(ct)
|
||||
}
|
||||
|
||||
pub fn decaps_and_mix<T: KEM, const SHK_LEN: usize>(
|
||||
pub fn decaps_and_mix<T: Kem<Error = Infallible>, const SHK_LEN: usize>(
|
||||
&mut self,
|
||||
sk: &[u8],
|
||||
pk: &[u8],
|
||||
@@ -1290,7 +1293,7 @@ impl HandshakeState {
|
||||
.copy_from_slice(self.ck.clone().danger_into_secret().secret());
|
||||
|
||||
// calculate ad contents
|
||||
let ad = lprf::biscuit_ad()?
|
||||
let ad = hash_domains::biscuit_ad()?
|
||||
.mix(srv.spkm.secret())?
|
||||
.mix(self.sidi.as_slice())?
|
||||
.mix(self.sidr.as_slice())?
|
||||
@@ -1325,7 +1328,7 @@ impl HandshakeState {
|
||||
let bk = BiscuitKeyPtr(((biscuit_ct[0] & 0b1000_0000) >> 7) as usize);
|
||||
|
||||
// Calculate additional data fields
|
||||
let ad = lprf::biscuit_ad()?
|
||||
let ad = hash_domains::biscuit_ad()?
|
||||
.mix(srv.spkm.secret())?
|
||||
.mix(sidi.as_slice())?
|
||||
.mix(sidr.as_slice())?
|
||||
@@ -1343,7 +1346,7 @@ impl HandshakeState {
|
||||
|
||||
// Reconstruct the biscuit fields
|
||||
let no = BiscuitId::from_slice(biscuit.biscuit_no());
|
||||
let ck = SecretPrfTree::danger_from_secret(Secret::from_slice(biscuit.ck())).dup();
|
||||
let ck = SecretHashDomain::danger_from_secret(Secret::from_slice(biscuit.ck())).dup();
|
||||
let pid = PeerId::from_slice(biscuit.pidi());
|
||||
|
||||
// Reconstruct the handshake state
|
||||
@@ -1370,8 +1373,8 @@ impl HandshakeState {
|
||||
|
||||
pub fn enter_live(self, srv: &CryptoServer, role: HandshakeRole) -> Result<Session> {
|
||||
let HandshakeState { ck, sidi, sidr } = self;
|
||||
let tki = ck.mix(&lprf::ini_enc()?)?.into_secret();
|
||||
let tkr = ck.mix(&lprf::res_enc()?)?.into_secret();
|
||||
let tki = ck.mix(&hash_domains::ini_enc()?)?.into_secret();
|
||||
let tkr = ck.mix(&hash_domains::res_enc()?)?.into_secret();
|
||||
let created_at = srv.timebase.now();
|
||||
let (ntx, nrx) = (0, 0);
|
||||
let (mysid, peersid, ktx, krx) = match role {
|
||||
@@ -1402,7 +1405,7 @@ impl CryptoServer {
|
||||
.get(self)
|
||||
.as_ref()
|
||||
.with_context(|| format!("No current session for peer {:?}", peer))?;
|
||||
Ok(session.ck.mix(&lprf::osk()?)?.into_secret())
|
||||
Ok(session.ck.mix(&hash_domains::osk()?)?.into_secret())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1424,7 +1427,7 @@ impl CryptoServer {
|
||||
ih.sidi_mut().copy_from_slice(&hs.core.sidi.value);
|
||||
|
||||
// IHI3
|
||||
EphemeralKEM::keygen(hs.eski.secret_mut(), &mut *hs.epki)?;
|
||||
EphemeralKem::keygen(hs.eski.secret_mut(), &mut *hs.epki)?;
|
||||
ih.epki_mut().copy_from_slice(&hs.epki.value);
|
||||
|
||||
// IHI4
|
||||
@@ -1432,7 +1435,7 @@ impl CryptoServer {
|
||||
|
||||
// IHI5
|
||||
hs.core
|
||||
.encaps_and_mix::<StaticKEM, { StaticKEM::SHK_LEN }>(
|
||||
.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||
ih.sctr_mut(),
|
||||
peer.get(self).spkt.secret(),
|
||||
)?;
|
||||
@@ -1471,7 +1474,7 @@ impl CryptoServer {
|
||||
core.mix(ih.sidi())?.mix(ih.epki())?;
|
||||
|
||||
// IHR5
|
||||
core.decaps_and_mix::<StaticKEM, { StaticKEM::SHK_LEN }>(
|
||||
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||
self.sskm.secret(),
|
||||
self.spkm.secret(),
|
||||
ih.sctr(),
|
||||
@@ -1501,10 +1504,10 @@ impl CryptoServer {
|
||||
core.mix(rh.sidr())?.mix(rh.sidi())?;
|
||||
|
||||
// RHR4
|
||||
core.encaps_and_mix::<EphemeralKEM, { EphemeralKEM::SHK_LEN }>(rh.ecti_mut(), ih.epki())?;
|
||||
core.encaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(rh.ecti_mut(), ih.epki())?;
|
||||
|
||||
// RHR5
|
||||
core.encaps_and_mix::<StaticKEM, { StaticKEM::SHK_LEN }>(
|
||||
core.encaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||
rh.scti_mut(),
|
||||
peer.get(self).spkt.secret(),
|
||||
)?;
|
||||
@@ -1569,14 +1572,14 @@ impl CryptoServer {
|
||||
core.mix(rh.sidr())?.mix(rh.sidi())?;
|
||||
|
||||
// RHI4
|
||||
core.decaps_and_mix::<EphemeralKEM, { EphemeralKEM::SHK_LEN }>(
|
||||
core.decaps_and_mix::<EphemeralKem, { EphemeralKem::SHK_LEN }>(
|
||||
hs!().eski.secret(),
|
||||
&*hs!().epki,
|
||||
rh.ecti(),
|
||||
)?;
|
||||
|
||||
// RHI5
|
||||
core.decaps_and_mix::<StaticKEM, { StaticKEM::SHK_LEN }>(
|
||||
core.decaps_and_mix::<StaticKem, { StaticKem::SHK_LEN }>(
|
||||
self.sskm.secret(),
|
||||
self.spkm.secret(),
|
||||
rh.scti(),
|
||||
@@ -1812,7 +1815,7 @@ mod test {
|
||||
fn keygen() -> Result<(SSk, SPk)> {
|
||||
// TODO: Copied from the benchmark; deduplicate
|
||||
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||
StaticKEM::keygen(sk.secret_mut(), pk.secret_mut())?;
|
||||
StaticKem::keygen(sk.secret_mut(), pk.secret_mut())?;
|
||||
Ok((sk, pk))
|
||||
}
|
||||
|
||||
|
||||
20
secret-memory/Cargo.toml
Normal file
20
secret-memory/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "rosenpass-secret-memory"
|
||||
version = "0.1.0"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal utilities for storing secrets in memory"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
rosenpass-to = { workspace = true }
|
||||
rosenpass-sodium = { workspace = true }
|
||||
rosenpass-util = { workspace = true }
|
||||
libsodium-sys-stable = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
5
secret-memory/readme.md
Normal file
5
secret-memory/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rosenpass secure memory library
|
||||
|
||||
Rosenpass internal library providing utilities for securely storing secret data in memory.
|
||||
|
||||
This is an internal library; not guarantee is made about its API at this point in time.
|
||||
20
secret-memory/src/debug.rs
Normal file
20
secret-memory/src/debug.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::fmt;
|
||||
|
||||
/// Writes the contents of an `&[u8]` as hexadecimal symbols to a [std::fmt::Formatter]
|
||||
pub fn debug_crypto_array(v: &[u8], fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str("[{}]=")?;
|
||||
if v.len() > 64 {
|
||||
for byte in &v[..32] {
|
||||
std::fmt::LowerHex::fmt(byte, fmt)?;
|
||||
}
|
||||
fmt.write_str("…")?;
|
||||
for byte in &v[v.len() - 32..] {
|
||||
std::fmt::LowerHex::fmt(byte, fmt)?;
|
||||
}
|
||||
} else {
|
||||
for byte in v {
|
||||
std::fmt::LowerHex::fmt(byte, fmt)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
7
secret-memory/src/file.rs
Normal file
7
secret-memory/src/file.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use std::path::Path;
|
||||
|
||||
pub trait StoreSecret {
|
||||
type Error;
|
||||
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::Error>;
|
||||
}
|
||||
9
secret-memory/src/lib.rs
Normal file
9
secret-memory/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
pub mod debug;
|
||||
pub mod file;
|
||||
pub mod rand;
|
||||
|
||||
mod public;
|
||||
pub use crate::public::Public;
|
||||
|
||||
mod secret;
|
||||
pub use crate::secret::Secret;
|
||||
112
secret-memory/src/public.rs
Normal file
112
secret-memory/src/public.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use crate::debug::debug_crypto_array;
|
||||
use rand::{Fill as Randomize, Rng};
|
||||
use rosenpass_to::{ops::copy_slice, To};
|
||||
use rosenpass_util::file::{fopen_r, LoadValue, ReadExactToEnd, StoreValue};
|
||||
use rosenpass_util::functional::mutating;
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::Path;
|
||||
|
||||
/// Contains information in the form of a byte array that may be known to the
|
||||
/// public
|
||||
// TODO: We should get rid of the Public type; just use a normal value
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(transparent)]
|
||||
pub struct Public<const N: usize> {
|
||||
pub value: [u8; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> Public<N> {
|
||||
/// Create a new [Public] from a byte slice
|
||||
pub fn from_slice(value: &[u8]) -> Self {
|
||||
copy_slice(value).to_this(|| Self::zero())
|
||||
}
|
||||
|
||||
/// Create a new [Public] from a byte array
|
||||
pub fn new(value: [u8; N]) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
/// Create a zero initialized [Public]
|
||||
pub fn zero() -> Self {
|
||||
Self { value: [0u8; N] }
|
||||
}
|
||||
|
||||
/// Create a random initialized [Public]
|
||||
pub fn random() -> Self {
|
||||
mutating(Self::zero(), |r| r.randomize())
|
||||
}
|
||||
|
||||
/// Randomize all bytes in an existing [Public]
|
||||
pub fn randomize(&mut self) {
|
||||
self.try_fill(&mut crate::rand::rng()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Randomize for Public<N> {
|
||||
fn try_fill<R: Rng + ?Sized>(&mut self, rng: &mut R) -> Result<(), rand::Error> {
|
||||
self.value.try_fill(rng)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for Public<N> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
debug_crypto_array(&self.value, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Deref for Public<N> {
|
||||
type Target = [u8; N];
|
||||
|
||||
fn deref(&self) -> &[u8; N] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DerefMut for Public<N> {
|
||||
fn deref_mut(&mut self) -> &mut [u8; N] {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Borrow<[u8; N]> for Public<N> {
|
||||
fn borrow(&self) -> &[u8; N] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
impl<const N: usize> BorrowMut<[u8; N]> for Public<N> {
|
||||
fn borrow_mut(&mut self) -> &mut [u8; N] {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Borrow<[u8]> for Public<N> {
|
||||
fn borrow(&self) -> &[u8] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
impl<const N: usize> BorrowMut<[u8]> for Public<N> {
|
||||
fn borrow_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> LoadValue for Public<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
||||
let mut v = Self::random();
|
||||
fopen_r(path)?.read_exact_to_end(&mut *v)?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> StoreValue for Public<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn store<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||
std::fs::write(path, **self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
5
secret-memory/src/rand.rs
Normal file
5
secret-memory/src/rand.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub type Rng = rand::rngs::ThreadRng;
|
||||
|
||||
pub fn rng() -> Rng {
|
||||
rand::thread_rng()
|
||||
}
|
||||
237
secret-memory/src/secret.rs
Normal file
237
secret-memory/src/secret.rs
Normal file
@@ -0,0 +1,237 @@
|
||||
use crate::file::StoreSecret;
|
||||
use anyhow::Context;
|
||||
use lazy_static::lazy_static;
|
||||
use rand::{Fill as Randomize, Rng};
|
||||
use rosenpass_sodium::alloc::{Alloc as SodiumAlloc, Box as SodiumBox, Vec as SodiumVec};
|
||||
use rosenpass_util::{
|
||||
b64::b64_reader,
|
||||
file::{fopen_r, LoadValue, LoadValueB64, ReadExactToEnd},
|
||||
functional::mutating,
|
||||
};
|
||||
use std::{collections::HashMap, convert::TryInto, fmt, path::Path, sync::Mutex};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
// This might become a problem in library usage; it's effectively a memory
|
||||
// leak which probably isn't a problem right now because most memory will
|
||||
// be reused…
|
||||
lazy_static! {
|
||||
static ref SECRET_CACHE: Mutex<SecretMemoryPool> = Mutex::new(SecretMemoryPool::new());
|
||||
}
|
||||
|
||||
/// Pool that stores secret memory allocations
|
||||
///
|
||||
/// Allocation of secret memory is expensive. Thus, this struct provides a
|
||||
/// pool of secret memory, readily available to yield protected, slices of
|
||||
/// memory.
|
||||
///
|
||||
/// Further information about the protection in place can be found in in the
|
||||
/// [libsodium documentation](https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations)
|
||||
#[derive(Debug)] // TODO check on Debug derive, is that clever
|
||||
struct SecretMemoryPool {
|
||||
pool: HashMap<usize, Vec<SodiumBox<[u8]>>>,
|
||||
}
|
||||
|
||||
impl SecretMemoryPool {
|
||||
/// Create a new [SecretMemoryPool]
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pool: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return secret back to the pool for future re-use
|
||||
pub fn release<const N: usize>(&mut self, mut sec: SodiumBox<[u8; N]>) {
|
||||
sec.zeroize();
|
||||
|
||||
// This conversion sequence is weird but at least it guarantees
|
||||
// that the heap allocation is preserved according to the docs
|
||||
let sec: SodiumVec<u8> = sec.into();
|
||||
let sec: SodiumBox<[u8]> = sec.into();
|
||||
|
||||
self.pool.entry(N).or_default().push(sec);
|
||||
}
|
||||
|
||||
/// Take protected memory from the pool, allocating new one if no suitable
|
||||
/// chunk is found in the inventory.
|
||||
///
|
||||
/// The secret is guaranteed to be full of nullbytes
|
||||
pub fn take<const N: usize>(&mut self) -> SodiumBox<[u8; N]> {
|
||||
let entry = self.pool.entry(N).or_default();
|
||||
match entry.pop() {
|
||||
None => SodiumBox::new_in([0u8; N], SodiumAlloc::default()),
|
||||
Some(sec) => sec.try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Storeage for a secret backed by [rosenpass_sodium::alloc::Alloc]
|
||||
pub struct Secret<const N: usize> {
|
||||
storage: Option<SodiumBox<[u8; N]>>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Secret<N> {
|
||||
pub fn from_slice(slice: &[u8]) -> Self {
|
||||
let mut new_self = Self::zero();
|
||||
new_self.secret_mut().copy_from_slice(slice);
|
||||
new_self
|
||||
}
|
||||
|
||||
/// Returns a new [Secret] that is zero initialized
|
||||
pub fn zero() -> Self {
|
||||
// Using [SecretMemoryPool] here because this operation is expensive,
|
||||
// yet it is used in hot loops
|
||||
Self {
|
||||
storage: Some(SECRET_CACHE.lock().unwrap().take()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new [Secret] that is randomized
|
||||
pub fn random() -> Self {
|
||||
mutating(Self::zero(), |r| r.randomize())
|
||||
}
|
||||
|
||||
/// Sets all data an existing secret to random bytes
|
||||
pub fn randomize(&mut self) {
|
||||
self.try_fill(&mut crate::rand::rng()).unwrap()
|
||||
}
|
||||
|
||||
/// Borrows the data
|
||||
pub fn secret(&self) -> &[u8; N] {
|
||||
self.storage.as_ref().unwrap()
|
||||
}
|
||||
|
||||
/// Borrows the data mutably
|
||||
pub fn secret_mut(&mut self) -> &mut [u8; N] {
|
||||
self.storage.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> ZeroizeOnDrop for Secret<N> {}
|
||||
impl<const N: usize> Zeroize for Secret<N> {
|
||||
fn zeroize(&mut self) {
|
||||
self.secret_mut().zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Randomize for Secret<N> {
|
||||
fn try_fill<R: Rng + ?Sized>(&mut self, rng: &mut R) -> Result<(), rand::Error> {
|
||||
// Zeroize self first just to make sure the barriers from the zeroize create take
|
||||
// effect to prevent the compiler from optimizing this away.
|
||||
// We should at some point replace this with our own barriers.
|
||||
self.zeroize();
|
||||
self.secret_mut().try_fill(rng)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Drop for Secret<N> {
|
||||
fn drop(&mut self) {
|
||||
self.storage
|
||||
.take()
|
||||
.map(|sec| SECRET_CACHE.lock().unwrap().release(sec));
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Clone for Secret<N> {
|
||||
fn clone(&self) -> Self {
|
||||
Self::from_slice(self.secret())
|
||||
}
|
||||
}
|
||||
|
||||
/// The Debug implementation of [Secret] does not reveal the secret data,
|
||||
/// instead a placeholder `<SECRET>` is used
|
||||
impl<const N: usize> fmt::Debug for Secret<N> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str("<SECRET>")
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> LoadValue for Secret<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
||||
let mut v = Self::random();
|
||||
let p = path.as_ref();
|
||||
fopen_r(p)?
|
||||
.read_exact_to_end(v.secret_mut())
|
||||
.with_context(|| format!("Could not load file {p:?}"))?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> LoadValueB64 for Secret<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn load_b64<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
|
||||
use std::io::Read;
|
||||
|
||||
let mut v = Self::random();
|
||||
let p = path.as_ref();
|
||||
// This might leave some fragments of the secret on the stack;
|
||||
// in practice this is likely not a problem because the stack likely
|
||||
// will be overwritten by something else soon but this is not exactly
|
||||
// guaranteed. It would be possible to remedy this, but since the secret
|
||||
// data will linger in the Linux page cache anyways with the current
|
||||
// implementation, going to great length to erase the secret here is
|
||||
// not worth it right now.
|
||||
b64_reader(&mut fopen_r(p)?)
|
||||
.read_exact(v.secret_mut())
|
||||
.with_context(|| format!("Could not load base64 file {p:?}"))?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> StoreSecret for Secret<N> {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||
std::fs::write(path, self.secret())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
/// check that we can alloc using the magic pool
|
||||
#[test]
|
||||
fn secret_memory_pool_take() {
|
||||
rosenpass_sodium::init().unwrap();
|
||||
const N: usize = 0x100;
|
||||
let mut pool = SecretMemoryPool::new();
|
||||
let secret: SodiumBox<[u8; N]> = pool.take();
|
||||
assert_eq!(secret.as_ref(), &[0; N]);
|
||||
}
|
||||
|
||||
/// check that a secrete lives, even if its [SecretMemoryPool] is deleted
|
||||
#[test]
|
||||
fn secret_memory_pool_drop() {
|
||||
rosenpass_sodium::init().unwrap();
|
||||
const N: usize = 0x100;
|
||||
let mut pool = SecretMemoryPool::new();
|
||||
let secret: SodiumBox<[u8; N]> = pool.take();
|
||||
std::mem::drop(pool);
|
||||
assert_eq!(secret.as_ref(), &[0; N]);
|
||||
}
|
||||
|
||||
/// check that a secrete can be reborn, freshly initialized with zero
|
||||
#[test]
|
||||
fn secret_memory_pool_release() {
|
||||
rosenpass_sodium::init().unwrap();
|
||||
const N: usize = 1;
|
||||
let mut pool = SecretMemoryPool::new();
|
||||
let mut secret: SodiumBox<[u8; N]> = pool.take();
|
||||
let old_secret_ptr = secret.as_ref().as_ptr();
|
||||
|
||||
secret.as_mut()[0] = 0x13;
|
||||
pool.release(secret);
|
||||
|
||||
// now check that we get the same ptr
|
||||
let new_secret: SodiumBox<[u8; N]> = pool.take();
|
||||
assert_eq!(old_secret_ptr, new_secret.as_ref().as_ptr());
|
||||
|
||||
// and that the secret was zeroized
|
||||
assert_eq!(new_secret.as_ref(), &[0; N]);
|
||||
}
|
||||
}
|
||||
@@ -15,3 +15,4 @@ rosenpass-to = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
libsodium-sys-stable = { workspace = true }
|
||||
log = { workspace = true }
|
||||
allocator-api2 = { workspace = true }
|
||||
|
||||
95
sodium/src/alloc/allocator.rs
Normal file
95
sodium/src/alloc/allocator.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use allocator_api2::alloc::{AllocError, Allocator, Layout};
|
||||
use libsodium_sys as libsodium;
|
||||
use std::fmt;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct AllocatorContents;
|
||||
|
||||
/// Memory allocation using sodium_malloc/sodium_free
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Alloc {
|
||||
_dummy_private_data: AllocatorContents,
|
||||
}
|
||||
|
||||
impl Alloc {
|
||||
pub fn new() -> Self {
|
||||
Alloc {
|
||||
_dummy_private_data: AllocatorContents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Allocator for Alloc {
|
||||
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||
// Call sodium allocator
|
||||
let ptr = unsafe { libsodium::sodium_malloc(layout.size()) };
|
||||
|
||||
// Ensure the right allocation is used
|
||||
let off = ptr.align_offset(layout.align());
|
||||
if off != 0 {
|
||||
log::error!("Allocation {layout:?} was requested but libsodium returned allocation \
|
||||
with offset {off} from the requested alignment. Libsodium always allocates values \
|
||||
at the end of a memory page for security reasons, custom alignments are not supported. \
|
||||
You could try allocating an oversized value.");
|
||||
return Err(AllocError);
|
||||
}
|
||||
|
||||
// Convert to a pointer size
|
||||
let ptr = core::ptr::slice_from_raw_parts_mut(ptr as *mut u8, layout.size());
|
||||
|
||||
// Conversion to a *const u8, then to a &[u8]
|
||||
match NonNull::new(ptr) {
|
||||
None => {
|
||||
log::error!(
|
||||
"Allocation {layout:?} was requested but libsodium returned a null pointer"
|
||||
);
|
||||
Err(AllocError)
|
||||
}
|
||||
Some(ret) => Ok(ret),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: Layout) {
|
||||
unsafe {
|
||||
libsodium::sodium_free(ptr.as_ptr() as *mut c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Alloc {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str("<libsodium based Rust allocator>")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
/// checks that the can malloc with libsodium
|
||||
#[test]
|
||||
fn sodium_allocation() {
|
||||
crate::init().unwrap();
|
||||
let alloc = Alloc::new();
|
||||
sodium_allocation_impl::<0>(&alloc);
|
||||
sodium_allocation_impl::<7>(&alloc);
|
||||
sodium_allocation_impl::<8>(&alloc);
|
||||
sodium_allocation_impl::<64>(&alloc);
|
||||
sodium_allocation_impl::<999>(&alloc);
|
||||
}
|
||||
|
||||
fn sodium_allocation_impl<const N: usize>(alloc: &Alloc) {
|
||||
crate::init().unwrap();
|
||||
let layout = Layout::new::<[u8; N]>();
|
||||
let mem = alloc.allocate(layout).unwrap();
|
||||
|
||||
// https://libsodium.gitbook.io/doc/memory_management#guarded-heap-allocations
|
||||
// promises us that allocated memory is initialized with the magic byte 0xDB
|
||||
assert_eq!(unsafe { mem.as_ref() }, &[0xDBu8; N]);
|
||||
|
||||
let mem = NonNull::new(mem.as_ptr() as *mut u8).unwrap();
|
||||
unsafe { alloc.deallocate(mem, layout) };
|
||||
}
|
||||
}
|
||||
10
sodium/src/alloc/mod.rs
Normal file
10
sodium/src/alloc/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
//! Access to sodium_malloc/sodium_free
|
||||
|
||||
mod allocator;
|
||||
pub use allocator::Alloc;
|
||||
|
||||
/// A box backed by sodium_malloc
|
||||
pub type Box<T> = allocator_api2::boxed::Box<T, Alloc>;
|
||||
|
||||
/// A vector backed by sodium_malloc
|
||||
pub type Vec<T> = allocator_api2::vec::Vec<T, Alloc>;
|
||||
@@ -26,27 +26,3 @@ pub fn increment(v: &mut [u8]) {
|
||||
libsodium::sodium_increment(v.as_mut_ptr(), v.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn randombytes_buf(buf: &mut [u8]) {
|
||||
unsafe { libsodium::randombytes_buf(buf.as_mut_ptr() as *mut c_void, buf.len()) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn memzero(buf: &mut [u8]) {
|
||||
unsafe { libsodium::sodium_memzero(buf.as_mut_ptr() as *mut c_void, buf.len()) };
|
||||
}
|
||||
|
||||
// Choose a fully random u64
|
||||
// TODO: Replace with ::rand::random
|
||||
pub fn rand_u64() -> u64 {
|
||||
let mut buf = [0u8; 8];
|
||||
randombytes_buf(&mut buf);
|
||||
u64::from_le_bytes(buf)
|
||||
}
|
||||
|
||||
// Choose a random f64 in [0; 1] inclusive; quick and dirty
|
||||
// TODO: Replace with ::rand::random
|
||||
pub fn rand_f64() -> f64 {
|
||||
(rand_u64() as f64) / (u64::MAX as f64)
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ pub fn init() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
pub mod aead;
|
||||
pub mod alloc;
|
||||
pub mod hash;
|
||||
pub mod helpers;
|
||||
|
||||
@@ -14,3 +14,4 @@ readme = "readme.md"
|
||||
[dependencies]
|
||||
base64 = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
rustix = { workspace = true }
|
||||
|
||||
12
util/src/fd.rs
Normal file
12
util/src/fd.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use std::os::fd::{OwnedFd, RawFd};
|
||||
|
||||
/// Clone some file descriptor
|
||||
///
|
||||
/// If the file descriptor is invalid, an error will be raised.
|
||||
pub fn claim_fd(fd: RawFd) -> anyhow::Result<OwnedFd> {
|
||||
use rustix::{fd::BorrowedFd, io::dup};
|
||||
|
||||
// This is safe since [dup] will simply raise
|
||||
let fd = unsafe { dup(BorrowedFd::borrow_raw(fd))? };
|
||||
Ok(fd)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod b64;
|
||||
pub mod fd;
|
||||
pub mod file;
|
||||
pub mod functional;
|
||||
pub mod mem;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use std::convert::Infallible;
|
||||
use std::result::Result;
|
||||
|
||||
/// Try block basically…returns a result and allows the use of the question mark operator inside
|
||||
#[macro_export]
|
||||
macro_rules! attempt {
|
||||
@@ -5,3 +8,86 @@ macro_rules! attempt {
|
||||
(|| -> ::anyhow::Result<_> { $block })()
|
||||
};
|
||||
}
|
||||
|
||||
/// Trait for container types that guarantee successful unwrapping.
|
||||
///
|
||||
/// The `.guaranteed()` function can be used over unwrap to show that
|
||||
/// the function will not panic.
|
||||
///
|
||||
/// Implementations must not panic.
|
||||
pub trait GuaranteedValue {
|
||||
type Value;
|
||||
|
||||
/// Extract the contained value while being panic-safe, like .unwrap()
|
||||
///
|
||||
/// # Panic Safety
|
||||
///
|
||||
/// Implementations of guaranteed() must not panic.
|
||||
fn guaranteed(self) -> Self::Value;
|
||||
}
|
||||
|
||||
/// A result type that never contains an error.
|
||||
///
|
||||
/// This is mostly useful in generic contexts.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::num::Wrapping;
|
||||
/// use std::result::Result;
|
||||
/// use std::convert::Infallible
|
||||
///
|
||||
/// trait FailableAddition {
|
||||
/// type Error;
|
||||
/// fn failable_addition(&self, other: &Self) -> Result<Self, Self::Error>;
|
||||
/// }
|
||||
///
|
||||
/// struct OverflowError;
|
||||
///
|
||||
/// impl<T> FailableAddition for Wrapping<T> {
|
||||
/// type Error = Infallible;
|
||||
/// fn failable_addition(&self, other: &Self) -> Guaranteed<Self> {
|
||||
/// self + other
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl<T> FailableAddition for u32 {
|
||||
/// type Error = Infallible;
|
||||
/// fn failable_addition(&self, other: &Self) -> Guaranteed<Self> {
|
||||
/// match self.checked_add(*other) {
|
||||
/// Some(v) => Ok(v),
|
||||
/// None => Err(OverflowError),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn failable_multiply<T>(a: &T, b: u32)
|
||||
/// -> Result<T, T::Error>
|
||||
/// where
|
||||
/// T: FailableAddition<Error> {
|
||||
/// let mut accu = a.failable_addition(a)?;
|
||||
/// for _ in ..(b-1) {
|
||||
/// accu.failable_addition(a)?;
|
||||
/// }
|
||||
/// Ok(accu)
|
||||
/// }
|
||||
///
|
||||
/// // We can use .guaranteed() with Wrapping<u32>, since the operation uses
|
||||
/// // the Infallible error type.
|
||||
/// // We can also use unwrap which just happens to not raise an error.
|
||||
/// assert_eq!(failable_multiply(&Wrapping::new(42u32), 3).guaranteed(), 126);
|
||||
/// assert_eq!(failable_multiply(&Wrapping::new(42u32), 3).unwrap(), 126);
|
||||
///
|
||||
/// // We can not use .guaranteed() with u32, since there can be an error.
|
||||
/// // We can however use unwrap(), which may panic
|
||||
/// assert_eq!(failable_multiply(&42u32, 3).guaranteed(), 126); // COMPILER ERROR
|
||||
/// assert_eq!(failable_multiply(&42u32, 3).unwrap(), 126);
|
||||
/// ```
|
||||
pub type Guaranteed<T> = Result<T, Infallible>;
|
||||
|
||||
impl<T> GuaranteedValue for Guaranteed<T> {
|
||||
type Value = T;
|
||||
fn guaranteed(self) -> Self::Value {
|
||||
self.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
42
wireguard-broker/Cargo.toml
Normal file
42
wireguard-broker/Cargo.toml
Normal file
@@ -0,0 +1,42 @@
|
||||
[package]
|
||||
name = "rosenpass-wireguard-broker"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Rosenpass internal broker that runs as root and supplies exchanged keys to the kernel."
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
rosenpass-lenses = { workspace = true }
|
||||
paste = { workspace = true } # TODO: Using lenses should not necessitate importing paste
|
||||
|
||||
# Privileged only
|
||||
wireguard-uapi = { workspace = true }
|
||||
|
||||
# Socket handler only
|
||||
rosenpass-to = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
# Mio broker client
|
||||
mio = { workspace = true }
|
||||
rosenpass-util = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "rosenpass-wireguard-broker-privileged"
|
||||
path = "src/bin/priviledged.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "rosenpass-wireguard-broker-socket-handler"
|
||||
test = false
|
||||
path = "src/bin/socket_handler.rs"
|
||||
doc = false
|
||||
5
wireguard-broker/readme.md
Normal file
5
wireguard-broker/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rosenpass internal broker supplying WireGuard with keys.
|
||||
|
||||
This crate contains a small application purpose-built to supply WireGuard in the linux kernel with pre-shared keys.
|
||||
|
||||
This is an internal library; not guarantee is made about its API at this point in time.
|
||||
152
wireguard-broker/src/api/client.rs
Normal file
152
wireguard-broker/src/api/client.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use std::{borrow::BorrowMut, marker::PhantomData};
|
||||
|
||||
use rosenpass_lenses::LenseView;
|
||||
|
||||
use crate::{
|
||||
api::msgs::{self, EnvelopeExt, SetPskRequestExt, SetPskResponseExt},
|
||||
WireGuardBroker,
|
||||
};
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BrokerClientPollResponseError<RecvError> {
|
||||
#[error(transparent)]
|
||||
IoError(RecvError),
|
||||
#[error("Invalid message.")]
|
||||
InvalidMessage,
|
||||
}
|
||||
|
||||
impl<RecvError> From<msgs::InvalidMessageTypeError> for BrokerClientPollResponseError<RecvError> {
|
||||
fn from(value: msgs::InvalidMessageTypeError) -> Self {
|
||||
let msgs::InvalidMessageTypeError = value; // Assert that this is a unit type
|
||||
BrokerClientPollResponseError::<RecvError>::InvalidMessage
|
||||
}
|
||||
}
|
||||
|
||||
fn io_pollerr<RecvError>(e: RecvError) -> BrokerClientPollResponseError<RecvError> {
|
||||
BrokerClientPollResponseError::<RecvError>::IoError(e)
|
||||
}
|
||||
|
||||
fn invalid_msg_pollerr<RecvError>() -> BrokerClientPollResponseError<RecvError> {
|
||||
BrokerClientPollResponseError::<RecvError>::InvalidMessage
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BrokerClientSetPskError<SendError> {
|
||||
#[error(transparent)]
|
||||
IoError(SendError),
|
||||
#[error("Interface name out of bounds")]
|
||||
IfaceOutOfBounds,
|
||||
}
|
||||
|
||||
pub trait BrokerClientIo {
|
||||
type SendError;
|
||||
type RecvError;
|
||||
|
||||
fn send_msg(&mut self, buf: &[u8]) -> Result<(), Self::SendError>;
|
||||
fn recv_msg(&mut self) -> Result<Option<&[u8]>, Self::RecvError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BrokerClient<'a, Io, IoRef>
|
||||
where
|
||||
Io: BrokerClientIo,
|
||||
IoRef: 'a + BorrowMut<Io>,
|
||||
{
|
||||
io: IoRef,
|
||||
_phantom_io: PhantomData<&'a mut Io>,
|
||||
}
|
||||
|
||||
impl<'a, Io, IoRef> BrokerClient<'a, Io, IoRef>
|
||||
where
|
||||
Io: BrokerClientIo,
|
||||
IoRef: 'a + BorrowMut<Io>,
|
||||
{
|
||||
pub fn new(io: IoRef) -> Self {
|
||||
Self {
|
||||
io,
|
||||
_phantom_io: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn io(&self) -> &IoRef {
|
||||
&self.io
|
||||
}
|
||||
|
||||
pub fn io_mut(&mut self) -> &mut IoRef {
|
||||
&mut self.io
|
||||
}
|
||||
|
||||
pub fn poll_response(
|
||||
&mut self,
|
||||
) -> Result<Option<msgs::SetPskResult>, BrokerClientPollResponseError<Io::RecvError>> {
|
||||
let res: &[u8] = match self.io.borrow_mut().recv_msg().map_err(io_pollerr)? {
|
||||
Some(r) => r,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let typ = res.get(0).ok_or(invalid_msg_pollerr())?;
|
||||
let typ = msgs::MsgType::try_from(*typ)?;
|
||||
let msgs::MsgType::SetPsk = typ; // Assert type
|
||||
|
||||
let res: msgs::Envelope<_, msgs::SetPskResponse<&[u8]>> = res
|
||||
.envelope_truncating()
|
||||
.map_err(|_| invalid_msg_pollerr())?;
|
||||
let res: msgs::SetPskResponse<&[u8]> = res
|
||||
.payload()
|
||||
.set_psk_response()
|
||||
.map_err(|_| invalid_msg_pollerr())?;
|
||||
let res: msgs::SetPskResponseReturnCode = res.return_code()[0]
|
||||
.try_into()
|
||||
.map_err(|_| invalid_msg_pollerr())?;
|
||||
let res: msgs::SetPskResult = res.into();
|
||||
|
||||
Ok(Some(res))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Io, IoRef> WireGuardBroker for BrokerClient<'a, Io, IoRef>
|
||||
where
|
||||
Io: BrokerClientIo,
|
||||
IoRef: 'a + BorrowMut<Io>,
|
||||
{
|
||||
type Error = BrokerClientSetPskError<Io::SendError>;
|
||||
|
||||
fn set_psk(
|
||||
&mut self,
|
||||
iface: &str,
|
||||
peer_id: [u8; 32],
|
||||
psk: [u8; 32],
|
||||
) -> Result<(), Self::Error> {
|
||||
use BrokerClientSetPskError::*;
|
||||
const BUF_SIZE: usize = <msgs::Envelope<(), msgs::SetPskRequest<()>> as LenseView>::LEN;
|
||||
|
||||
// Allocate message
|
||||
let mut req = [0u8; BUF_SIZE];
|
||||
|
||||
// Construct message view
|
||||
let mut req: msgs::Envelope<_, msgs::SetPskRequest<&mut [u8]>> =
|
||||
(&mut req as &mut [u8]).envelope_truncating().unwrap();
|
||||
|
||||
// Populate envelope
|
||||
req.msg_type_mut()
|
||||
.copy_from_slice(&[msgs::MsgType::SetPsk as u8]);
|
||||
{
|
||||
// Derived payload
|
||||
let mut req: msgs::SetPskRequest<&mut [u8]> =
|
||||
req.payload_mut().set_psk_request().unwrap();
|
||||
|
||||
// Populate payload
|
||||
req.peer_id_mut().copy_from_slice(&peer_id);
|
||||
req.psk_mut().copy_from_slice(&psk);
|
||||
req.set_iface(iface).ok_or(IfaceOutOfBounds)?;
|
||||
}
|
||||
|
||||
// Send message
|
||||
self.io
|
||||
.borrow_mut()
|
||||
.send_msg(req.all_bytes())
|
||||
.map_err(IoError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
204
wireguard-broker/src/api/mio_client.rs
Normal file
204
wireguard-broker/src/api/mio_client.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::io::{ErrorKind, Read, Write};
|
||||
|
||||
use anyhow::{bail, ensure};
|
||||
|
||||
use crate::WireGuardBroker;
|
||||
|
||||
use super::client::{
|
||||
BrokerClient, BrokerClientIo, BrokerClientPollResponseError, BrokerClientSetPskError,
|
||||
};
|
||||
use super::msgs;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MioBrokerClient {
|
||||
inner: BrokerClient<'static, MioBrokerClientIo, MioBrokerClientIo>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MioBrokerClientIo {
|
||||
socket: mio::net::UnixStream,
|
||||
send_buf: VecDeque<u8>,
|
||||
receiving_size: bool,
|
||||
recv_buf: Vec<u8>,
|
||||
recv_off: usize,
|
||||
}
|
||||
|
||||
impl MioBrokerClient {
|
||||
pub fn new(socket: mio::net::UnixStream) -> Self {
|
||||
let io = MioBrokerClientIo {
|
||||
socket,
|
||||
send_buf: VecDeque::new(),
|
||||
receiving_size: false,
|
||||
recv_buf: Vec::new(),
|
||||
recv_off: 0,
|
||||
};
|
||||
let inner = BrokerClient::new(io);
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub fn poll(&mut self) -> anyhow::Result<Option<msgs::SetPskResult>> {
|
||||
self.inner.io_mut().flush()?;
|
||||
|
||||
// This sucks
|
||||
match self.inner.poll_response() {
|
||||
Ok(res) => {
|
||||
return Ok(res);
|
||||
}
|
||||
Err(BrokerClientPollResponseError::IoError(e)) => {
|
||||
return Err(e);
|
||||
}
|
||||
Err(BrokerClientPollResponseError::InvalidMessage) => {
|
||||
bail!("Invalid message");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl WireGuardBroker for MioBrokerClient {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn set_psk(&mut self, iface: &str, peer_id: [u8; 32], psk: [u8; 32]) -> anyhow::Result<()> {
|
||||
use BrokerClientSetPskError::*;
|
||||
let e = self.inner.set_psk(iface, peer_id, psk);
|
||||
match e {
|
||||
Ok(()) => Ok(()),
|
||||
Err(IoError(e)) => Err(e),
|
||||
Err(IfaceOutOfBounds) => bail!("Interface name size is out of bounds."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BrokerClientIo for MioBrokerClientIo {
|
||||
type SendError = anyhow::Error;
|
||||
type RecvError = anyhow::Error;
|
||||
|
||||
fn send_msg(&mut self, buf: &[u8]) -> Result<(), Self::SendError> {
|
||||
self.flush()?;
|
||||
self.send_or_buffer(&(buf.len() as u64).to_le_bytes())?;
|
||||
self.send_or_buffer(&buf)?;
|
||||
self.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv_msg(&mut self) -> Result<Option<&[u8]>, Self::RecvError> {
|
||||
// Stale message in receive buffer. Reset!
|
||||
if self.recv_off == self.recv_buf.len() {
|
||||
self.receiving_size = true;
|
||||
self.recv_off = 0;
|
||||
self.recv_buf.resize(8, 0);
|
||||
}
|
||||
|
||||
// Try filling the receive buffer
|
||||
self.recv_off += raw_recv(&self.socket, &mut self.recv_buf[self.recv_off..])?;
|
||||
if self.recv_off < self.recv_buf.len() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Received size, now start receiving
|
||||
if self.receiving_size {
|
||||
// Received the size
|
||||
// Parse the received length
|
||||
let len: &[u8; 8] = self.recv_buf[..].try_into().unwrap();
|
||||
let len: usize = u64::from_le_bytes(*len) as usize;
|
||||
|
||||
ensure!(
|
||||
len <= msgs::RESPONSE_MSG_BUFFER_SIZE,
|
||||
"Oversized buffer ({len}) in psk buffer response."
|
||||
);
|
||||
|
||||
// Prepare the message buffer for receiving an actual message of the given size
|
||||
self.receiving_size = false;
|
||||
self.recv_off = 0;
|
||||
self.recv_buf.resize(len, 0);
|
||||
|
||||
// Try to receive the message
|
||||
return self.recv_msg();
|
||||
}
|
||||
|
||||
// Received an actual message
|
||||
return Ok(Some(&self.recv_buf[..]));
|
||||
}
|
||||
}
|
||||
|
||||
impl MioBrokerClientIo {
|
||||
fn flush(&mut self) -> anyhow::Result<()> {
|
||||
let (fst, snd) = self.send_buf.as_slices();
|
||||
|
||||
let (written, res) = match raw_send(&self.socket, fst) {
|
||||
Ok(w1) if w1 >= fst.len() => match raw_send(&self.socket, snd) {
|
||||
Ok(w2) => (w1 + w2, Ok(())),
|
||||
Err(e) => (w1, Err(e)),
|
||||
},
|
||||
Ok(w1) => (w1, Ok(())),
|
||||
Err(e) => (0, Err(e)),
|
||||
};
|
||||
|
||||
self.send_buf.drain(..written);
|
||||
|
||||
(&self.socket).try_io(|| (&self.socket).flush())?;
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn send_or_buffer(&mut self, buf: &[u8]) -> anyhow::Result<()> {
|
||||
let mut off = 0;
|
||||
|
||||
if self.send_buf.is_empty() {
|
||||
off += raw_send(&self.socket, buf)?;
|
||||
}
|
||||
|
||||
self.send_buf.extend((&buf[off..]).iter());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_send(mut socket: &mio::net::UnixStream, data: &[u8]) -> anyhow::Result<usize> {
|
||||
let mut off = 0;
|
||||
|
||||
socket.try_io(|| {
|
||||
loop {
|
||||
if off == data.len() {
|
||||
return Ok(());
|
||||
}
|
||||
match socket.write(&data[off..]) {
|
||||
Ok(n) => {
|
||||
off += n;
|
||||
}
|
||||
Err(e) if e.kind() == ErrorKind::Interrupted => {
|
||||
// pass – retry
|
||||
}
|
||||
Err(e) if off > 0 || e.kind() == ErrorKind::WouldBlock => return Ok(()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
return Ok(off);
|
||||
}
|
||||
|
||||
fn raw_recv(mut socket: &mio::net::UnixStream, out: &mut [u8]) -> anyhow::Result<usize> {
|
||||
let mut off = 0;
|
||||
|
||||
socket.try_io(|| {
|
||||
loop {
|
||||
if off == out.len() {
|
||||
return Ok(());
|
||||
}
|
||||
match socket.read(&mut out[off..]) {
|
||||
Ok(n) => {
|
||||
off += n;
|
||||
}
|
||||
Err(e) if e.kind() == ErrorKind::Interrupted => {
|
||||
// pass – retry
|
||||
}
|
||||
Err(e) if off > 0 || e.kind() == ErrorKind::WouldBlock => return Ok(()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
return Ok(off);
|
||||
}
|
||||
4
wireguard-broker/src/api/mod.rs
Normal file
4
wireguard-broker/src/api/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod client;
|
||||
pub mod mio_client;
|
||||
pub mod msgs;
|
||||
pub mod server;
|
||||
140
wireguard-broker/src/api/msgs.rs
Normal file
140
wireguard-broker/src/api/msgs.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
use std::result::Result;
|
||||
use std::str::{from_utf8, Utf8Error};
|
||||
|
||||
use rosenpass_lenses::{lense, LenseView};
|
||||
|
||||
pub const REQUEST_MSG_BUFFER_SIZE: usize = <Envelope<(), SetPskRequest<()>> as LenseView>::LEN;
|
||||
pub const RESPONSE_MSG_BUFFER_SIZE: usize = <Envelope<(), SetPskResponse<()>> as LenseView>::LEN;
|
||||
|
||||
lense! { Envelope<M> :=
|
||||
/// [MsgType] of this message
|
||||
msg_type: 1,
|
||||
/// Reserved for future use
|
||||
reserved: 3,
|
||||
/// The actual Paylod
|
||||
payload: M::LEN
|
||||
}
|
||||
|
||||
lense! { SetPskRequest :=
|
||||
peer_id: 32,
|
||||
psk: 32,
|
||||
iface_size: 1, // TODO: We should have variable length strings in lenses
|
||||
iface_buf: 255
|
||||
}
|
||||
|
||||
impl SetPskRequest<&[u8]> {
|
||||
pub fn iface_bin(&self) -> &[u8] {
|
||||
let len = self.iface_size()[0] as usize;
|
||||
&self.iface_buf()[..len]
|
||||
}
|
||||
|
||||
pub fn iface(&self) -> Result<&str, Utf8Error> {
|
||||
from_utf8(self.iface_bin())
|
||||
}
|
||||
}
|
||||
|
||||
impl SetPskRequest<&mut [u8]> {
|
||||
pub fn set_iface_bin(&mut self, iface: &[u8]) -> Option<()> {
|
||||
(iface.len() < 256).then_some(())?; // Assert iface.len() < 256
|
||||
|
||||
self.iface_size_mut()[0] = iface.len() as u8;
|
||||
|
||||
self.iface_buf_mut().fill(0);
|
||||
(&mut self.iface_buf_mut()[..iface.len()]).copy_from_slice(iface);
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn set_iface(&mut self, iface: &str) -> Option<()> {
|
||||
self.set_iface_bin(iface.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
lense! { SetPskResponse :=
|
||||
return_code: 1
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum SetPskError {
|
||||
#[error("The wireguard pre-shared-key assignment broker experienced an internal error.")]
|
||||
InternalError,
|
||||
#[error("The indicated wireguard interface does not exist")]
|
||||
NoSuchInterface,
|
||||
#[error("The indicated peer does not exist on the wireguard interface")]
|
||||
NoSuchPeer,
|
||||
}
|
||||
|
||||
pub type SetPskResult = Result<(), SetPskError>;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
pub enum SetPskResponseReturnCode {
|
||||
Success = 0x00,
|
||||
InternalError = 0x01,
|
||||
NoSuchInterface = 0x02,
|
||||
NoSuchPeer = 0x03,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||
pub struct InvalidSetPskResponseError;
|
||||
|
||||
impl TryFrom<u8> for SetPskResponseReturnCode {
|
||||
type Error = InvalidSetPskResponseError;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
use SetPskResponseReturnCode::*;
|
||||
match value {
|
||||
0x00 => Ok(Success),
|
||||
0x01 => Ok(InternalError),
|
||||
0x02 => Ok(NoSuchInterface),
|
||||
0x03 => Ok(NoSuchPeer),
|
||||
_ => Err(InvalidSetPskResponseError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SetPskResponseReturnCode> for SetPskResult {
|
||||
fn from(value: SetPskResponseReturnCode) -> Self {
|
||||
use SetPskError as E;
|
||||
use SetPskResponseReturnCode as C;
|
||||
match value {
|
||||
C::Success => Ok(()),
|
||||
C::InternalError => Err(E::InternalError),
|
||||
C::NoSuchInterface => Err(E::NoSuchInterface),
|
||||
C::NoSuchPeer => Err(E::NoSuchPeer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SetPskResult> for SetPskResponseReturnCode {
|
||||
fn from(value: SetPskResult) -> Self {
|
||||
use SetPskError as E;
|
||||
use SetPskResponseReturnCode as C;
|
||||
match value {
|
||||
Ok(()) => C::Success,
|
||||
Err(E::InternalError) => C::InternalError,
|
||||
Err(E::NoSuchInterface) => C::NoSuchInterface,
|
||||
Err(E::NoSuchPeer) => C::NoSuchPeer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
pub enum MsgType {
|
||||
SetPsk = 0x01,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||
pub struct InvalidMessageTypeError;
|
||||
|
||||
impl TryFrom<u8> for MsgType {
|
||||
type Error = InvalidMessageTypeError;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x01 => Ok(MsgType::SetPsk),
|
||||
_ => Err(InvalidMessageTypeError),
|
||||
}
|
||||
}
|
||||
}
|
||||
99
wireguard-broker/src/api/server.rs
Normal file
99
wireguard-broker/src/api/server.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
use std::borrow::BorrowMut;
|
||||
use std::marker::PhantomData;
|
||||
use std::result::Result;
|
||||
|
||||
use rosenpass_lenses::LenseError;
|
||||
|
||||
use crate::api::msgs::{self, EnvelopeExt, SetPskRequestExt, SetPskResponseExt};
|
||||
use crate::WireGuardBroker;
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
|
||||
pub enum BrokerServerError {
|
||||
#[error("No such request type: {}", .0)]
|
||||
NoSuchRequestType(u8),
|
||||
#[error("Invalid message received.")]
|
||||
InvalidMessage,
|
||||
}
|
||||
|
||||
impl From<LenseError> for BrokerServerError {
|
||||
fn from(value: LenseError) -> Self {
|
||||
use BrokerServerError as Be;
|
||||
use LenseError as Le;
|
||||
match value {
|
||||
Le::BufferSizeMismatch => Be::InvalidMessage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<msgs::InvalidMessageTypeError> for BrokerServerError {
|
||||
fn from(value: msgs::InvalidMessageTypeError) -> Self {
|
||||
let msgs::InvalidMessageTypeError = value; // Assert that this is a unit type
|
||||
BrokerServerError::InvalidMessage
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BrokerServer<'a, Err, Inner, Ref>
|
||||
where
|
||||
msgs::SetPskError: From<Err>,
|
||||
Inner: WireGuardBroker<Error = Err>,
|
||||
Ref: BorrowMut<Inner> + 'a,
|
||||
{
|
||||
inner: Ref,
|
||||
_phantom: PhantomData<&'a mut Inner>,
|
||||
}
|
||||
|
||||
impl<'a, Err, Inner, Ref> BrokerServer<'a, Err, Inner, Ref>
|
||||
where
|
||||
msgs::SetPskError: From<Err>,
|
||||
Inner: WireGuardBroker<Error = Err>,
|
||||
Ref: 'a + BorrowMut<Inner>,
|
||||
{
|
||||
pub fn new(inner: Ref) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_message(
|
||||
&mut self,
|
||||
req: &[u8],
|
||||
res: &mut [u8; msgs::RESPONSE_MSG_BUFFER_SIZE],
|
||||
) -> Result<usize, BrokerServerError> {
|
||||
use BrokerServerError::*;
|
||||
|
||||
let typ = req.get(0).ok_or(InvalidMessage)?;
|
||||
let typ = msgs::MsgType::try_from(*typ)?;
|
||||
let msgs::MsgType::SetPsk = typ; // Assert type
|
||||
|
||||
let req: msgs::Envelope<_, msgs::SetPskRequest<&[u8]>> = req.envelope_truncating()?;
|
||||
let mut res: msgs::Envelope<_, msgs::SetPskResponse<&mut [u8]>> =
|
||||
(res as &mut [u8]).envelope_truncating()?;
|
||||
(&mut res).msg_type_mut()[0] = msgs::MsgType::SetPsk as u8;
|
||||
self.handle_set_psk(
|
||||
req.payload().set_psk_request()?,
|
||||
res.payload_mut().set_psk_response()?,
|
||||
)?;
|
||||
Ok(res.all_bytes().len())
|
||||
}
|
||||
|
||||
fn handle_set_psk(
|
||||
&mut self,
|
||||
req: msgs::SetPskRequest<&[u8]>,
|
||||
mut res: msgs::SetPskResponse<&mut [u8]>,
|
||||
) -> Result<(), BrokerServerError> {
|
||||
// Using unwrap here since lenses can not return fixed-size arrays
|
||||
// TODO: Slices should give access to fixed size arrays
|
||||
let r: Result<(), Err> = self.inner.borrow_mut().set_psk(
|
||||
req.iface()
|
||||
.map_err(|_e| BrokerServerError::InvalidMessage)?,
|
||||
req.peer_id().try_into().unwrap(),
|
||||
req.psk().try_into().unwrap(),
|
||||
);
|
||||
let r: msgs::SetPskResult = r.map_err(|e| e.into());
|
||||
let r: msgs::SetPskResponseReturnCode = r.into();
|
||||
res.return_code_mut()[0] = r as u8;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
56
wireguard-broker/src/bin/priviledged.rs
Normal file
56
wireguard-broker/src/bin/priviledged.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use std::io::{stdin, stdout, Read, Write};
|
||||
use std::result::Result;
|
||||
|
||||
use rosenpass_wireguard_broker::api::msgs;
|
||||
use rosenpass_wireguard_broker::api::server::BrokerServer;
|
||||
use rosenpass_wireguard_broker::netlink as wg;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum BrokerAppError {
|
||||
#[error(transparent)]
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
WgConnectError(#[from] wg::ConnectError),
|
||||
#[error(transparent)]
|
||||
WgSetPskError(#[from] wg::SetPskError),
|
||||
#[error("Oversized message {}; something about the request is fatally wrong", .0)]
|
||||
OversizedMessage(u64),
|
||||
}
|
||||
|
||||
fn main() -> Result<(), BrokerAppError> {
|
||||
let mut broker = BrokerServer::new(wg::NetlinkWireGuardBroker::new()?);
|
||||
|
||||
let mut stdin = stdin().lock();
|
||||
let mut stdout = stdout().lock();
|
||||
loop {
|
||||
// Read the message length
|
||||
let mut len = [0u8; 8];
|
||||
stdin.read_exact(&mut len)?;
|
||||
|
||||
// Parse the message length
|
||||
let len = u64::from_le_bytes(len);
|
||||
if (len as usize) > msgs::REQUEST_MSG_BUFFER_SIZE {
|
||||
return Err(BrokerAppError::OversizedMessage(len));
|
||||
}
|
||||
|
||||
// Read the message itself
|
||||
let mut req_buf = [0u8; msgs::REQUEST_MSG_BUFFER_SIZE];
|
||||
let req_buf = &mut req_buf[..(len as usize)];
|
||||
stdin.read_exact(req_buf)?;
|
||||
|
||||
// Process the message
|
||||
let mut res_buf = [0u8; msgs::RESPONSE_MSG_BUFFER_SIZE];
|
||||
let res = match broker.handle_message(req_buf, &mut res_buf) {
|
||||
Ok(len) => &res_buf[..len],
|
||||
Err(e) => {
|
||||
eprintln!("Error processing message for wireguard PSK broker: {e:?}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Write the response
|
||||
stdout.write_all(&(res.len() as u64).to_le_bytes())?;
|
||||
stdout.write_all(&res)?;
|
||||
stdout.flush()?;
|
||||
}
|
||||
}
|
||||
191
wireguard-broker/src/bin/socket_handler.rs
Normal file
191
wireguard-broker/src/bin/socket_handler.rs
Normal file
@@ -0,0 +1,191 @@
|
||||
use std::process::Stdio;
|
||||
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{UnixListener, UnixStream};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio::task;
|
||||
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use clap::{ArgGroup, Parser};
|
||||
|
||||
use rosenpass_util::fd::claim_fd;
|
||||
use rosenpass_wireguard_broker::api::msgs;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[clap(group(
|
||||
ArgGroup::new("socket")
|
||||
.required(true)
|
||||
.args(&["listen_path", "listen_fd", "stream_fd"]),
|
||||
))]
|
||||
struct Args {
|
||||
/// Where in the file-system to create the unix socket this broker will be listening for
|
||||
/// connections on
|
||||
#[arg(long)]
|
||||
listen_path: Option<String>,
|
||||
|
||||
/// When this broker is called from another process, the other process can open and bind the
|
||||
/// unix socket 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)]
|
||||
listen_fd: Option<i32>,
|
||||
|
||||
/// When this broker is called from another process, the other process can connect the unix socket
|
||||
/// themselves, for instance using the `socketpair(2)` system call.
|
||||
#[arg(long)]
|
||||
stream_fd: Option<i32>,
|
||||
|
||||
/// The underlying broker, accepting commands through stdin and sending results through stdout.
|
||||
#[arg(
|
||||
last = true,
|
||||
allow_hyphen_values = true,
|
||||
default_value = "rosenpass-wireguard-broker-privileged"
|
||||
)]
|
||||
command: Vec<String>,
|
||||
}
|
||||
|
||||
struct BrokerRequest {
|
||||
reply_to: oneshot::Sender<BrokerResponse>,
|
||||
request: Vec<u8>,
|
||||
}
|
||||
|
||||
struct BrokerResponse {
|
||||
response: Vec<u8>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
let (proc_tx, proc_rx) = mpsc::channel(100);
|
||||
|
||||
// Start the inner broker handler
|
||||
task::spawn(async move {
|
||||
if let Err(e) = direct_broker_process(proc_rx, args.command).await {
|
||||
log::error!("Error in broker command handler: {e}");
|
||||
panic!("Can not proceed without underlying broker process");
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for incoming requests
|
||||
if let Some(path) = args.listen_path {
|
||||
let sock = UnixListener::bind(path)?;
|
||||
listen_for_clients(proc_tx, sock).await
|
||||
} else if let Some(fd) = args.listen_fd {
|
||||
let sock = std::os::unix::net::UnixListener::from(claim_fd(fd)?);
|
||||
sock.set_nonblocking(true)?;
|
||||
listen_for_clients(proc_tx, UnixListener::from_std(sock)?).await
|
||||
} else if let Some(fd) = args.stream_fd {
|
||||
let stream = std::os::unix::net::UnixStream::from(claim_fd(fd)?);
|
||||
stream.set_nonblocking(true)?;
|
||||
on_accept(proc_tx, UnixStream::from_std(stream)?).await
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
async fn direct_broker_process(
|
||||
mut queue: mpsc::Receiver<BrokerRequest>,
|
||||
cmd: Vec<String>,
|
||||
) -> Result<()> {
|
||||
let proc = Command::new(&cmd[0])
|
||||
.args(&cmd[1..])
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let mut stdin = proc.stdin.unwrap();
|
||||
let mut stdout = proc.stdout.unwrap();
|
||||
|
||||
loop {
|
||||
let BrokerRequest { reply_to, request } = queue.recv().await.unwrap();
|
||||
|
||||
stdin
|
||||
.write_all(&(request.len() as u64).to_le_bytes())
|
||||
.await?;
|
||||
stdin.write_all(&request[..]).await?;
|
||||
|
||||
// Read the response length
|
||||
let mut len = [0u8; 8];
|
||||
stdout.read_exact(&mut len).await?;
|
||||
|
||||
// Parse the response length
|
||||
let len = u64::from_le_bytes(len) as usize;
|
||||
ensure!(
|
||||
len <= msgs::RESPONSE_MSG_BUFFER_SIZE,
|
||||
"Oversized buffer ({len}) in broker stdout."
|
||||
);
|
||||
|
||||
// Read the message itself
|
||||
let mut res_buf = request; // Avoid allocating memory if we don't have to
|
||||
res_buf.resize(len as usize, 0);
|
||||
stdout.read_exact(&mut res_buf[..len]).await?;
|
||||
|
||||
// Return to the unix socket connection worker
|
||||
reply_to
|
||||
.send(BrokerResponse { response: res_buf })
|
||||
.or_else(|_| bail!("Unable to send respnse to unix socket worker."))?;
|
||||
}
|
||||
}
|
||||
|
||||
async fn listen_for_clients(queue: mpsc::Sender<BrokerRequest>, sock: UnixListener) -> Result<()> {
|
||||
loop {
|
||||
let (stream, _addr) = sock.accept().await?;
|
||||
let queue = queue.clone();
|
||||
task::spawn(async move {
|
||||
if let Err(e) = on_accept(queue, stream).await {
|
||||
log::error!("Error during connection processing: {e}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE: If loop can ever terminate we need to join the spawned tasks
|
||||
}
|
||||
|
||||
async fn on_accept(queue: mpsc::Sender<BrokerRequest>, mut stream: UnixStream) -> Result<()> {
|
||||
let mut req_buf = Vec::new();
|
||||
|
||||
loop {
|
||||
stream.readable().await?;
|
||||
|
||||
// Read the message length
|
||||
let mut len = [0u8; 8];
|
||||
stream.read_exact(&mut len).await?;
|
||||
|
||||
// Parse the message length
|
||||
let len = u64::from_le_bytes(len) as usize;
|
||||
ensure!(
|
||||
len <= msgs::REQUEST_MSG_BUFFER_SIZE,
|
||||
"Oversized buffer ({len}) in unix socket input."
|
||||
);
|
||||
|
||||
// Read the message itself
|
||||
req_buf.resize(len as usize, 0);
|
||||
stream.read_exact(&mut req_buf[..len]).await?;
|
||||
|
||||
// Handle the message
|
||||
let (reply_tx, reply_rx) = oneshot::channel();
|
||||
queue
|
||||
.send(BrokerRequest {
|
||||
reply_to: reply_tx,
|
||||
request: req_buf,
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Wait for the reply
|
||||
let BrokerResponse { response } = reply_rx.await.unwrap();
|
||||
|
||||
// Write reply back to unix socket
|
||||
stream
|
||||
.write_all(&(response.len() as u64).to_le_bytes())
|
||||
.await?;
|
||||
stream.write_all(&response[..]).await?;
|
||||
stream.flush().await?;
|
||||
|
||||
// Reuse the same memory for the next message
|
||||
req_buf = response;
|
||||
}
|
||||
}
|
||||
15
wireguard-broker/src/lib.rs
Normal file
15
wireguard-broker/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use std::result::Result;
|
||||
|
||||
pub trait WireGuardBroker {
|
||||
type Error;
|
||||
|
||||
fn set_psk(
|
||||
&mut self,
|
||||
interface: &str,
|
||||
peer_id: [u8; 32],
|
||||
psk: [u8; 32],
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
pub mod api;
|
||||
pub mod netlink;
|
||||
103
wireguard-broker/src/netlink.rs
Normal file
103
wireguard-broker/src/netlink.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use wireguard_uapi::linux as wg;
|
||||
|
||||
use crate::api::msgs;
|
||||
use crate::WireGuardBroker;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ConnectError {
|
||||
#[error(transparent)]
|
||||
ConnectError(#[from] wg::err::ConnectError),
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum NetlinkError {
|
||||
#[error(transparent)]
|
||||
SetDevice(#[from] wg::err::SetDeviceError),
|
||||
#[error(transparent)]
|
||||
GetDevice(#[from] wg::err::GetDeviceError),
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SetPskError {
|
||||
#[error("The indicated wireguard interface does not exist")]
|
||||
NoSuchInterface,
|
||||
#[error("The indicated peer does not exist on the wireguard interface")]
|
||||
NoSuchPeer,
|
||||
#[error(transparent)]
|
||||
NetlinkError(#[from] NetlinkError),
|
||||
}
|
||||
|
||||
impl From<wg::err::SetDeviceError> for SetPskError {
|
||||
fn from(err: wg::err::SetDeviceError) -> Self {
|
||||
NetlinkError::from(err).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<wg::err::GetDeviceError> for SetPskError {
|
||||
fn from(err: wg::err::GetDeviceError) -> Self {
|
||||
NetlinkError::from(err).into()
|
||||
}
|
||||
}
|
||||
|
||||
use msgs::SetPskError as SetPskMsgsError;
|
||||
use SetPskError as SetPskNetlinkError;
|
||||
impl From<SetPskNetlinkError> for SetPskMsgsError {
|
||||
fn from(err: SetPskError) -> Self {
|
||||
match err {
|
||||
SetPskNetlinkError::NoSuchPeer => SetPskMsgsError::NoSuchPeer,
|
||||
_ => SetPskMsgsError::InternalError,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetlinkWireGuardBroker {
|
||||
sock: wg::WgSocket,
|
||||
}
|
||||
|
||||
impl NetlinkWireGuardBroker {
|
||||
pub fn new() -> Result<Self, ConnectError> {
|
||||
let sock = wg::WgSocket::connect()?;
|
||||
Ok(Self { sock })
|
||||
}
|
||||
}
|
||||
|
||||
impl WireGuardBroker for NetlinkWireGuardBroker {
|
||||
type Error = SetPskError;
|
||||
|
||||
fn set_psk(
|
||||
&mut self,
|
||||
interface: &str,
|
||||
peer_id: [u8; 32],
|
||||
psk: [u8; 32],
|
||||
) -> Result<(), Self::Error> {
|
||||
// Ensure that the peer exists by querying the device configuration
|
||||
// TODO: Use InvalidInterfaceError
|
||||
let state = self
|
||||
.sock
|
||||
.get_device(wg::DeviceInterface::from_name(interface.to_owned()))?;
|
||||
|
||||
if state
|
||||
.peers
|
||||
.iter()
|
||||
.find(|p| &p.public_key == &peer_id)
|
||||
.is_none()
|
||||
{
|
||||
return Err(SetPskError::NoSuchPeer);
|
||||
}
|
||||
|
||||
// Peer update description
|
||||
let mut set_peer = wireguard_uapi::set::Peer::from_public_key(&peer_id);
|
||||
set_peer
|
||||
.flags
|
||||
.push(wireguard_uapi::linux::set::WgPeerF::UpdateOnly);
|
||||
set_peer.preshared_key = Some(&psk);
|
||||
|
||||
// Device update description
|
||||
let mut set_dev = wireguard_uapi::set::Device::from_ifname(interface.to_owned());
|
||||
set_dev.peers.push(set_peer);
|
||||
|
||||
self.sock.set_device(set_dev)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user