Compare commits

...

279 Commits

Author SHA1 Message Date
Karolin Varner
b8e9519e26 chore: Ignore rust advisory RUSTSEC-2023-0089
error[unmaintained]: atomic-polyfill is unmaintained
   ┌─ /github/workspace/Cargo.lock:15:1
   │
15 │ atomic-polyfill 1.0.3 registry+https://github.com/rust-lang/crates.io-index
   │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unmaintained advisory detected
   │
   ├ ID: RUSTSEC-2023-0089
   ├ Advisory: https://rustsec.org/advisories/RUSTSEC-2023-0089
   ├ The author has archived the GitHub repository and mentions deprecation in
     project's
     [README](48e55c1666/README.md).

     ## Possible alternatives

      * [portable-atomic](https://crates.io/crates/portable-atomic)
   ├ Announcement: 48e55c1666
   ├ Solution: No safe upgrade is available!
   ├ atomic-polyfill v1.0.3
     └── heapless v0.7.17
         ├── aead v0.5.2
         │   └── chacha20poly1305 v0.10.1
         │       └── rosenpass-ciphers v0.1.0
         │           ├── rosenpass v0.3.0-dev
         │           │   ├── rosenpass-fuzzing v0.0.1
         │           │   └── rp v0.2.1
         │           ├── rosenpass-fuzzing v0.0.1 (*)
         │           └── rp v0.2.1 (*)
         └── postcard v1.1.1
             └── rosenpass-wireguard-broker v0.1.0
                 ├── rosenpass v0.3.0-dev (*)
                 └── rp v0.2.1 (*)
2025-05-09 18:15:55 +02:00
Karolin Varner
c3def9744f fix(ci+supply-chain+dependabot): Checkout correct branch in the supply chain checks for cargo-vet (#645)
Some checks failed
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (warp-macos-13-arm64-6x) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Has been cancelled
Supply-Chain / Supply Chain Report (push) Has been cancelled
Supply-Chain / Vet Dependencies (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Build Docker Images / docker-image-rp (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rp (arm64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (arm64) (push) Has been cancelled
Build Docker Images / merge-digests (rosenpass) (push) Has been cancelled
Build Docker Images / merge-digests (rp) (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.default (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.release-package (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2025-04-23 10:26:57 +02:00
David Niehues
e3d3584adb fix(ci+supply-chain+dependabot): Checkout correct branch in the supply chain checks for cargo-vet 2025-04-22 13:18:26 +02:00
Karolin Varner
a1982e0245 ci(cargo-vet): merge regeneration of exemptions for cargo-vet for dependabot into main cargo-vet job (#643)
Some checks failed
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (ubicloud-standard-2-ubuntu-2204) (push) Has been cancelled
QC / cargo-test (warp-macos-13-arm64-6x) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Has been cancelled
Supply-Chain / Supply Chain Report (push) Has been cancelled
Supply-Chain / Vet Dependencies (push) Has been cancelled
Build Docker Images / docker-image-rp (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rp (arm64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (arm64) (push) Has been cancelled
Build Docker Images / merge-digests (rosenpass) (push) Has been cancelled
Build Docker Images / merge-digests (rp) (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.default (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.release-package (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2025-04-14 16:06:09 +02:00
David Niehues
4896cd6130 ci(cargo-vet): merge regeneration of exemptions for cargo-vet for dependabot into main cargo-vet job 2025-04-14 12:19:08 +02:00
Karolin Varner
9aab9d2d2a enable github workflow for creating crev-exemptions for dependabots to push changes to PR (#642) 2025-04-14 11:33:49 +02:00
David Niehues
108ca440fe enable github workflow for creating crev-exemptions for dependabots to push to the repository 2025-04-14 11:30:36 +02:00
Karolin Varner
03e408b7c2 ci(cargo-crev): Fix regeneration of cargo-crev-exemptions (#641) 2025-04-14 11:05:44 +02:00
David Niehues
67f387a190 ci(cargo-crev): Fix regeneration of cargo-crev-exemptions 2025-04-14 09:35:35 +02:00
Karolin Varner
745c3962bb Merge Set MSRV (#638)
Some checks are pending
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC Mac / cargo-test-mac (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (ubicloud-standard-2-ubuntu-2204) (push) Waiting to run
QC / cargo-test (warp-macos-13-arm64-6x) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Waiting to run
Supply-Chain / Supply Chain Report (push) Waiting to run
Supply-Chain / Vet Dependencies (push) Waiting to run
2025-04-13 13:46:16 +02:00
Karolin Varner
f6971aa5ad feat: Set rust-toolchain file to use 1.77.0
At @wucke13's request to facilitate a later nix oxalica integration.

https://github.com/oxalica/rust-overlay
2025-04-13 13:44:36 +02:00
Karolin Varner
b46cd636d2 fix: Security update – crossbeam-channel 2025-04-13 13:44:36 +02:00
Karolin Varner
f22f4aad7d feat: Fix minimum supported cargo version to 1.77
This should ensure, that our Cargo.lock file stays at version 3
when using `cargo update` or dependabot.
2025-04-13 13:44:36 +02:00
Karolin Varner
a83589d76a feat: Cargo-msrv in full development package 2025-04-13 13:44:36 +02:00
Karolin Varner
508d46f2bc fix: Deadlock for manual Mac CI runs parallelism 2025-04-13 13:44:25 +02:00
Karolin Varner
3fc3083a54 feat: Manual Mac CI runs parallelism 2025-04-13 13:35:28 +02:00
Karolin Varner
faa45a8540 fix: Incorrect permissions for manual mac CI workflow try 2 2025-04-13 13:25:56 +02:00
Karolin Varner
77632d0725 fix: Incorrect permissions for manual mac CI workflow 2025-04-13 13:18:54 +02:00
Karolin Varner
7218b0a3f4 feat: Ability to manually run CI for pull requests 2025-04-13 13:12:58 +02:00
Karolin Varner
4266cbfb72 fix(time): Fix another non-functional test for Timebase
Some checks failed
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (ubicloud-standard-2-ubuntu-2204) (push) Has been cancelled
QC / cargo-test (warp-macos-13-arm64-6x) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Supply-Chain / Vet Dependencies (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.rosenpass-oci-image (push) Has been cancelled
Build Docker Images / docker-image-rp (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rp (arm64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (arm64) (push) Has been cancelled
Build Docker Images / merge-digests (rosenpass) (push) Has been cancelled
Build Docker Images / merge-digests (rp) (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.default (push) Has been cancelled
Nix on Mac / Build aarch64-darwin.release-package (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2025-04-09 08:39:10 +02:00
Karolin Varner
070d299329 fix(ci): Separate names of cargo test jobs on linux and mac
Some checks are pending
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC Mac / cargo-test-mac (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-test (ubicloud-standard-2-ubuntu-2204) (push) Waiting to run
QC / cargo-test (warp-macos-13-arm64-6x) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Waiting to run
Supply-Chain / Supply Chain Report (push) Waiting to run
Supply-Chain / Vet Dependencies (push) Waiting to run
2025-04-09 08:28:58 +02:00
Karolin Varner
15699710a0 ci(supply-chain+dependabot): Automatically create exemptions for cargo-crev for dependa-bot PRs (#635) 2025-04-09 08:11:57 +02:00
David Niehues
ae418ffba7 ci(supply-chain+dependabot): Automatically create exemptions for cargo-crev for dependa-bot PRs 2025-04-09 07:59:20 +02:00
Karolin Varner
e3f7773bac fix(time): Remove non-functional test causing errors on mac os
There actually is no reason why now being time 0.0 would be incorrect;
it might just mean a low resolution clock is being used.
2025-04-09 01:29:21 +02:00
Karolin Varner
9ab754eb0b fix(docker): Used name of author not of org for docker upload 2025-04-09 01:21:23 +02:00
Karolin Varner
b055457d01 Sha3 use in Rosenpass, Trait for Crypto Primitives, and Libcrux Crypto Backend (#632) 2025-04-09 01:01:40 +02:00
Karolin Varner
b3403e7120 fix(ci): Do not run mac os CI jobs on pull requests
Warpbuild is quite expensive
2025-04-09 00:18:55 +02:00
Karolin Varner
abd5210ae4 fix(ci): Memcmp not constant time on apple sillicon, stopgap
https://github.com/rosenpass/rosenpass/issues/634
2025-04-09 00:12:00 +02:00
Karolin Varner
03464e1be7 feat(ci): Use warpbuild based runners for mac os 2025-04-08 23:54:48 +02:00
Karolin Varner
54fc904c15 fix(rp): Protocol version field should be optional 2025-04-08 23:54:48 +02:00
Karolin Varner
ceff8b711a feat(ci): Use ubicloud based, paid for runners 2025-04-08 23:54:48 +02:00
Karolin Varner
c84bbed3bd fix: Increase time outs for integration tests 2025-04-08 18:14:45 +02:00
Karolin Varner
d453002230 fix: Security update for tokio 2025-04-08 18:14:45 +02:00
Karolin Varner
e81612d2e3 fix: Incorrect timeouts for poll_example
- Raised first timeout under load to fourty seconds
- Corrected discrepancies between debug prints and numeric checks
2025-04-08 18:14:45 +02:00
Karolin Varner
d558bdb633 fix: Add a feature flag for the cookie reply mechanism
This is a stopgap measure against #539
2025-04-08 17:51:37 +02:00
Karolin Varner
e8fb7206fc fix: Wrong host identification in poll_example 2025-04-08 17:34:11 +02:00
David Niehues
b47d3a9deb style(ciphers): fix formatting 2025-04-05 17:31:32 +02:00
David Niehues
f7fb09bc44 ci(supply-chain): update exemptions for cargo-vet 2025-04-05 17:24:08 +02:00
David Niehues
db6530ef77 doc(rosenpass): properly document protocol function for hash domains 2025-04-05 17:14:18 +02:00
David Niehues
8f519b042d dev(rosenpass): adapt protocol identifier for protocol version v 0.2 to be backwards compatible with current main branch 2025-04-05 17:09:25 +02:00
Jan Winkelmann (keks)
954162b61f cleanup 2025-04-03 17:04:00 +02:00
Jan Winkelmann (keks)
c65abe7bd9 fix dos test: hardcode use of shake in seal_cookie 2025-04-03 16:55:03 +02:00
Karolin Varner
80885d81d7 fix: Missing nix hashes for libcrux_blake 2025-04-03 16:55:03 +02:00
Jan Winkelmann (keks)
d023108d3b attempt to work around the importCargoLock bugs 2025-04-03 16:55:03 +02:00
Jan Winkelmann (keks)
417df7aa7f update the lock file 2025-04-03 16:55:03 +02:00
Jan Winkelmann (keks)
9dd00e04c1 Use libcrux-blake2 with std
This way we don't require the error_in_core feature of the Rust compiler
2025-04-03 16:55:03 +02:00
David Niehues
1a8e220aa8 ci(supply-chain): Add exceptions for advisories RUSTSEC-2024-0436 and RUSTSEC-2024-0370 to cargo-deny 2025-04-03 16:55:03 +02:00
David Niehues
de0022f092 test(rosenpass): Adapt test for protocol_version in config to work with and without feature "experiment_api" 2025-04-03 16:55:03 +02:00
David Niehues
dbb891a2ed ci(supply-chain): Regenerate exemptions for cargo-vet 2025-04-03 16:55:03 +02:00
David Niehues
531ae0ef70 test(rosenpass): Adapt test for protocol version of config files to tests being run with --all-features 2025-04-03 16:55:03 +02:00
David Niehues
8bb54b9cca doc(ciphers): correct formatting 2025-04-03 16:55:03 +02:00
David Niehues
7566eadef8 doc(rosenpass): correct formatting 2025-04-03 16:55:03 +02:00
David Niehues
ebf6403ea7 doc(ciphers + rosenpass): improve the documentation 2025-04-03 16:55:03 +02:00
David Niehues
62d408eade dev(ciphers): implement the Display trait for the KeyedHash that allows to choose a hash. 2025-04-03 16:55:03 +02:00
David Niehues
d1cf6af531 test(rosenpass): Add test for protocol version in a toml configuration. 2025-04-03 16:55:03 +02:00
David Niehues
5e6c85d73d test(rosenpass): Complete support for SHAKE256 in gen-ipc-msg-types.rs 2025-04-03 16:55:03 +02:00
David Niehues
3205f8c572 doc(rosenpass): Remove already done TODO in handshake.rs 2025-04-03 16:55:03 +02:00
David Niehues
b21a95dbbd doc(rp+rosenpass+ciphers+cipher-traits): Apply cargo fmt formatting 2025-04-03 16:55:03 +02:00
Jan Winkelmann (keks)
006946442a Fix doc code examples in oqs Kem macro 2025-04-03 16:55:03 +02:00
David Niehues
33901d598a test(ciphers): Adapt SHAKE256 tests to longer including the output length. 2025-04-03 16:55:03 +02:00
David Niehues
944be10bd2 dev(rp): Adapt rp to include set a protocol version. 2025-04-03 16:55:03 +02:00
David Niehues
23cf60c7ec dev(rosenpass): Make the cooke mechenism use SHA3 exclusively 2025-04-03 16:55:03 +02:00
David Niehues
6f71767529 dev(ciphers): remove keyed hash module 2025-04-03 16:55:03 +02:00
Jan Winkelmann (keks)
38f371e3d7 Fix examples in Doc-Comments 2025-04-03 16:15:03 +02:00
Jan Winkelmann (keks)
2dba9205e7 Address Feedback 2025-04-03 16:14:55 +02:00
Jan Winkelmann (keks)
30c3de3f87 undo add submodule 2025-04-03 16:14:55 +02:00
Jan Winkelmann (keks)
b16619b1d3 fix doc example tests 2025-04-03 16:14:51 +02:00
Jan Winkelmann (keks)
576ad5f6d0 respect experiment_libcrux_blake2 feature flag 2025-04-03 16:14:47 +02:00
Jan Winkelmann (keks)
6494518460 add fine-grained features 2025-04-03 16:14:19 +02:00
Jan Winkelmann (keks)
185e92108e add blake2 from libcrux 2025-04-03 16:14:19 +02:00
Jan Winkelmann (keks)
253243a8c8 add kyber512 from libcrux 2025-04-03 16:14:19 +02:00
Jan Winkelmann (keks)
075d9ffff3 update libcrux chachapoly to use libcrux-chacha20poly1305 2025-04-03 16:14:19 +02:00
Jan Winkelmann (keks)
01a1408044 address feedback 2025-04-03 16:12:44 +02:00
Jan Winkelmann (keks)
b84e0beae8 introduce traits for all the primitives and algorithms. A bit more cleanup. 2025-04-03 16:12:44 +02:00
Jan Winkelmann (keks)
949a3e4d23 Add &self receiver to KEM trait methods 2025-04-03 16:12:44 +02:00
Jan Winkelmann (keks)
d61b137761 update KEM trait 2025-04-03 16:12:44 +02:00
Jan Winkelmann (keks)
a1f41953b7 Reorganize the ciphers crate 2025-04-03 16:12:23 +02:00
Jan Winkelmann (keks)
46ebb6f46c Remove algorithm traits for now 2025-04-03 16:11:55 +02:00
Jan Winkelmann (keks)
32ae8f7051 Rename hash selection enum to KeyedHash, restructure traits 2025-04-03 16:11:55 +02:00
Jan Winkelmann (keks)
b94ddd980d remove superfluous associated types 2025-04-03 16:11:55 +02:00
Jan Winkelmann (keks)
44e46895aa fmt 2025-04-03 15:57:43 +02:00
David Niehues
2ddd1488b3 doc(rosenpass): fix typo 2025-03-19 11:29:11 +01:00
David Niehues
c9aad280b2 test(rosenpass): adapt gen-ipc-msg-types to fully go through. Explicit test for SHAKE256 still missing 2025-03-19 11:29:11 +01:00
David Niehues
d7398d9bcf doc(rosenpass): fix typo 2025-03-19 11:29:11 +01:00
David Niehues
6d25c13fd1 dev(ciphers): make the libcrux implementation of chachapoly return an error instead of panicking when decryption fails. This makes tests decryptions possible. 2025-03-19 11:29:11 +01:00
David Niehues
2d2d109246 dev(rosenpass): add support for the shake256 hash function in the rosenpass crate 2025-03-19 11:29:11 +01:00
David Niehues
30e158f594 dev(ciphers): change HashDomain and related structures to use EitherShakeOrBlake. Docu pending 2025-03-19 11:29:11 +01:00
David Niehues
cf74584f51 tests(ciphers): add rudimentary tests for the shake256 implementation 2025-03-19 11:29:11 +01:00
David Niehues
793cfd227f dev(ciphers): provide EitherShakeOrBlake for 32 bytes KEY_LEN and 32 bytes of HASH_LEN based on SHAKE256 and the incorrect blake2b-hmac 2025-03-19 11:29:11 +01:00
David Niehues
54c8e91db4 doc(ciphers): fix typo in comment 2025-03-19 11:29:11 +01:00
David Niehues
1b0179e751 dev(ciphers): provide implementations of KeyedHash and KeyedHashInstance for the incorrect hmac for blake2b. 2025-03-19 11:29:11 +01:00
David Niehues
760ecdc457 dev(ciphers): add EitherHash enum and thus the functionality for choosing a hash function at runtime 2025-03-19 11:29:11 +01:00
David Niehues
6a9bbddde3 dev(ciphers): move blake2b.rs and incorrect_hmac_blake2b.rs to dedicated hash_functions directory 2025-03-19 11:29:11 +01:00
David Niehues
530f81b9d5 dev(ciphers): use InferredHash to provide KeyedHashInstance for SHAKE256 2025-03-19 11:29:11 +01:00
David Niehues
b96df1588c dev(ciphers): add InferredKeyedHash to instantiate KeyedHashFunctions generically 2025-03-19 11:29:11 +01:00
David Niehues
5a2555a327 dev(ciphers): add implementation of shake256 2025-03-19 11:29:11 +01:00
David Niehues
ac3f21c4bd dev: add sha3 dependency 2025-03-19 11:29:11 +01:00
David Niehues
b36d30d89d dev(cipher-traits): add KeyedHash(Instance) traits 2025-03-19 11:29:11 +01:00
Paul Spooren
62fe529d36 ci(docker): Merge multi-platform job
Some checks failed
QC / Rust Format (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Has been cancelled
Supply-Chain / Supply Chain Report (push) Has been cancelled
Supply-Chain / Vet Dependencies (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Build Docker Images / docker-image-rp (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rp (arm64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (arm64) (push) Has been cancelled
Build Docker Images / merge-digests (rosenpass) (push) Has been cancelled
Build Docker Images / merge-digests (rp) (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
Based on the Docker reference:
https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners

Signed-off-by: Paul Spooren <mail@aparcar.org>
2025-03-18 15:43:23 +01:00
Paul Spooren
76d01ffaf9 ci(docker): use GitHub native file change tracking
Don't pull in an external action but rely on GitHubs native way to
detect file changes. Also fix a logic flaw where a PR would try to push
an image (but never succeed due to missing secrets).

Co-authored-by: Benjamin Lipp <blipp@rosenpass.eu>
Signed-off-by: Paul Spooren <mail@aparcar.org>
2025-03-18 15:43:23 +01:00
Karolin Varner
576b17cd9c feat(docker): change write permission on docker build workflow to have write permission to packages (#616)
Some checks failed
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Supply-Chain / Supply Chain Report (push) Has been cancelled
Supply-Chain / Vet Dependencies (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
Build Docker Images / build-and-test-rp (amd64) (push) Has been cancelled
Build Docker Images / build-and-test-rp (arm64) (push) Has been cancelled
Build Docker Images / docker-image-rp (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rp (arm64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (amd64) (push) Has been cancelled
Build Docker Images / docker-image-rosenpass (arm64) (push) Has been cancelled
2025-02-26 17:46:21 +01:00
Amin Faez
cbc1bb4be2 feat(docker): change write permission on docker build workflow and fix its change filter 2025-02-26 16:41:55 +01:00
Karolin Varner
c8a084157e feat(docker): add .docker/Dockerfile, .docker/README.md and workflow building and publishing docker images (#582) 2025-02-26 15:50:17 +01:00
Amin Faez
09f1353dcc feat(docker): rename .docker to docker 2025-02-26 15:44:05 +01:00
Amin Faez
43225c1fe8 feat(docker): fix docker build workflow conditional checks 2025-02-26 09:15:38 +01:00
Amin Faez
8e41cfc0b4 feat(docker): remove stray quote, check if docker related files changes before running workflow 2025-02-26 00:05:37 +01:00
Amin Faez
69538622b4 feat(docker): remove qemu from the second build and push job in the docker build workflow 2025-02-25 16:45:19 +01:00
Amin Faez
45a7c17cdd feat(docker): fix runs on designation to ubuntu-24.04-arm 2025-02-25 16:22:29 +01:00
Amin Faez
b8ecdab8dc feat(docker): docker build workflow integration test now compares the resulting key with sudo 2025-02-25 13:03:56 +01:00
Amin Faez
af9d83b472 feat(docker): change the docker integration test workflow to wait until the shared key file is generated 2025-02-25 12:56:30 +01:00
Amin Faez
f81e329a11 feat(docker): fix the integration test workflow 2025-02-25 12:33:29 +01:00
Amin Faez
5e2c72ef99 feat(docker): add integration test to the build docker images workflow 2025-02-25 12:19:45 +01:00
Amin Faez
88e7d1d1cb feat(docker): remove additional labels from Dockerfile
feat(docker): rename the docker usage guide
feat(docker): reference the usage guide
feat(docker): change the github workflow to build the arm images natively
2025-02-25 12:09:18 +01:00
Amin Faez
43a930d3f7 feat(docker): fix docker image names
feat(docker): add tag based on commit hash,
feat(docker): add arm64 platform for docker images
2025-02-25 12:09:18 +01:00
Amin Faez
b5f6d07650 feat(docker): add .docker/Dockerfile, .docker/README.md and workflow building and publishing docker images 2025-02-25 12:09:18 +01:00
Karolin Varner
be3c3d3d61 fix: avoid duplicate crates (#612)
Some checks are pending
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
Nix / Build i686-linux.default (push) Blocked by required conditions
Nix / Build i686-linux.rosenpass (push) Waiting to run
Nix / Build i686-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Run Nix checks on i686-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Waiting to run
Supply-Chain / Supply Chain Report (push) Waiting to run
Supply-Chain / Vet Dependencies (push) Waiting to run
2025-02-25 10:23:13 +01:00
Dimitris Apostolou
fe60cea959 fix: avoid duplicate crates 2025-02-24 13:48:31 +02:00
Karolin Varner
441988cf43 chore: cargo update (#609)
Some checks failed
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Has been cancelled
Supply-Chain / Supply Chain Report (push) Has been cancelled
Supply-Chain / Vet Dependencies (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2025-02-22 21:24:20 +01:00
Karolin Varner
b40b7f4f2f chore: cargo update
- Had to remove the test checking for manpages to be generated for
  the keygen command since clap-mangen disabled creating manpages
  for hidden commands.
  d96cc71626
- Had to pin home to the previous version because it now requires a
  new rust version without major version update
- Changed util/src/fd tests due to false positives in CI
  > note: panic did not contain expected string
  >      panic message: `"fd != -1"`,
  > expected substring: `"fd != u32::MAX as RawFd"`
2025-02-22 17:45:34 +01:00
Karolin Varner
da76d88170 WP2: Create DEB&RPM packages and test in debian/ubuntu/fedora (#535)
Some checks are pending
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Waiting to run
Supply-Chain / Supply Chain Report (push) Waiting to run
Supply-Chain / Vet Dependencies (push) Waiting to run
2025-02-22 15:01:24 +01:00
Jacek Galowicz
e35955f99c fix release workflow 2025-02-09 15:19:55 +00:00
Jacek Galowicz
87587399ed Drop nix channels as we're not using channels anyway. 2025-02-09 21:39:24 +07:00
Jacek Galowicz
9fdba31b32 Build and upload DEB and RPM artefacts 2025-02-09 21:39:24 +07:00
Jacek Galowicz
0bfe47e5b8 fix naming typo 2025-02-09 21:39:24 +07:00
Jacek Galowicz
771dce3ac7 Use latest naming scheme of upstream flake 2025-02-09 21:39:24 +07:00
Jacek Galowicz
436c6e6f87 use https 2025-02-09 21:39:24 +07:00
Jacek Galowicz
f093406c34 Use upstream nix-vm-test after PR was merged 2025-02-09 21:39:24 +07:00
Jacek Galowicz
eadf70ee38 Generate and test RPM package for Fedora 2025-02-09 21:39:24 +07:00
Jacek Galowicz
7ac0883970 Generate and test .deb package for Debian and Ubuntu 2025-02-09 21:39:24 +07:00
Paul Spooren
b1658b83a0 chore(CI): add github actions for supply chain protection (#579)
Some checks failed
Nix / Run Nix checks on x86_64-linux (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Supply-Chain / Deny dependencies with vulnerabilities or incompatible licenses (push) Has been cancelled
Supply-Chain / Supply Chain Report (push) Has been cancelled
Supply-Chain / Vet Dependencies (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
2025-02-06 08:48:27 +00:00
David Niehues
27650e95a7 doc(ci): add documentation for supply chain protection 2025-02-06 08:18:17 +01:00
David Niehues
6ab4e1152c chore(ci): add cargo-vet to the CI for supply-chain protection. 2025-02-06 08:18:17 +01:00
David Niehues
2c64da23f1 chore(ci): add cargo-supply-chain to the CI for supply-chain protection. 2025-02-06 08:18:17 +01:00
David Niehues
03cc609a1e chore(ci): add cargo-deny to the CI for supply-chain protection. 2025-02-06 08:18:17 +01:00
David Niehues
3effcb313e chore: update criterion to remediate security advisories. Fixes #596 2025-02-06 08:17:38 +01:00
Karolin Varner
fded3b2e79 chore: cargo-audit in nix develop .#fullEnv (#597)
Some checks failed
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
Nix / Build i686-linux.rosenpass (push) Has been cancelled
Nix / Run Nix checks on i686-linux (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass (push) Has been cancelled
Nix / Build x86_64-darwin.rp (push) Has been cancelled
Nix / Run Nix checks on x86_64-darwin (push) Has been cancelled
Nix / Build x86_64-linux.proverif-patched (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass (push) Has been cancelled
Nix / Build aarch64-linux.rp (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
2025-02-04 19:37:12 +01:00
Karolin Varner
1471bb6a9f chore: cargo-audit in nix develop .#fullEnv 2025-02-04 19:36:56 +01:00
Karolin Varner
7edf84bd4a fix: Accidental removed line of comment (#595) 2025-02-04 19:36:42 +01:00
Karolin Varner
5187e50bb7 fix: Accidental removed line of comment
9bae080c4d

Co-Authored-By: @phildremi
2025-02-04 18:37:38 +01:00
Karolin Varner
fd5806ba55 fix(deps): fix crate vulnerabilities (#571) 2025-02-04 18:36:11 +01:00
Dimitris Apostolou
8e50d38b38 fix(deps): fix crate vulnerabilities 2025-02-04 17:20:40 +02:00
Karolin Varner
377f2f40d2 fix: Input dependent memory access in statistical constant time execu… (#586) 2025-02-04 16:12:43 +01:00
Karolin Varner
9bae080c4d fix: Input dependent memory access in statistical constant time execution test
Supplying different memory locations to the memcmp function
in the test is not constant time; this was an issue wit
the test and not with memcmp itself.

The issue mainly showed up in the Release build where the
correlation coefficient was in the ballpark of just below
-0.01 with enough variance to sometimes but not usually fail
the test. The precise reason for this is unknown but some
optimization applied in the release build is most likely the
culprit.

This should increase the stability of our CI which occasionally
was flaky.
2025-02-04 13:34:19 +01:00
Karolin Varner
3392da5163 chore: Fix CI (#585)
Some checks failed
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Build x86_64-linux.whitepaper (push) Has been cancelled
Nix / Run Nix checks on x86_64-linux (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2025-02-03 20:38:09 +01:00
Karolin Varner
3109cf1ffc chore: Fix CI 2025-02-03 19:58:14 +01:00
dependabot[bot]
d2539e445f build(deps): bump serde from 1.0.216 to 1.0.217 (#570)
Some checks failed
Nix / Build x86_64-linux.rosenpass-static (push) Has been cancelled
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Build x86_64-linux.whitepaper (push) Has been cancelled
Nix / Run Nix checks on x86_64-linux (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.216 to 1.0.217.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.216...v1.0.217)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-29 17:48:54 +01:00
dependabot[bot]
6dc58cc6c1 build(deps): bump anyhow from 1.0.94 to 1.0.95 (#569)
Some checks failed
Nix / Build aarch64-linux.rp (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static (push) Has been cancelled
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Build x86_64-linux.whitepaper (push) Has been cancelled
Nix / Run Nix checks on x86_64-linux (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.94 to 1.0.95.
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.94...1.0.95)

---
updated-dependencies:
- dependency-name: anyhow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-25 10:47:12 +01:00
Karolin Varner
e3d16966c9 Add documentation and tests for the build_crypto_server module (#568)
Some checks failed
Nix / Run Nix checks on i686-linux (push) Waiting to run
Nix / Build x86_64-darwin.default (push) Blocked by required conditions
Nix / Build x86_64-darwin.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
2024-12-21 17:02:02 +01:00
Philipp Dresselmann
a5e6af4b49 chore(docs): Add docstrings for the build_crypto_server module 2024-12-21 00:35:26 +01:00
Karolin Varner
24a71977f0 API Doc and a few tests for rosenpass::api (#566)
Some checks failed
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
Nix / Build i686-linux.rosenpass (push) Has been cancelled
Nix / Run Nix checks on i686-linux (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass (push) Has been cancelled
Nix / Build x86_64-darwin.rp (push) Has been cancelled
Nix / Run Nix checks on x86_64-darwin (push) Has been cancelled
Nix / Build x86_64-linux.proverif-patched (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass (push) Has been cancelled
Nix / Build aarch64-linux.rp (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static (push) Has been cancelled
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Build x86_64-linux.whitepaper (push) Has been cancelled
Nix / Run Nix checks on x86_64-linux (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
2024-12-20 09:24:57 +01:00
Karolin Varner
5f0ac579d7 chore: Documentation and few tests for rosenpass::api 2024-12-19 19:42:09 +01:00
Karolin Varner
4df994b5f0 fix: Coverage reporting in API integration tests 2024-12-19 19:42:09 +01:00
Karolin Varner
e4e0a9e661 chore: Example on how to use to use the Rosenpass API 2024-12-19 19:42:09 +01:00
Karolin Varner
742e037936 chore: Smoketests for rosenpass-gen-ipc-msg-types 2024-12-19 19:42:09 +01:00
Karolin Varner
b5848af799 chore: Smoketests for rp command (#565)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-19 15:11:08 +01:00
Karolin Varner
4982e40084 chore: Smoketests for rp 2024-12-19 15:00:08 +01:00
Karolin Varner
c1ae3268c6 Add a missing cleanup step to the coverage script (#564) 2024-12-19 14:59:51 +01:00
Paul Spooren
524ec68f3f Add a docstring example for mio/uds_send_fd (#563)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-19 13:07:13 +01:00
Philipp Dresselmann
184603aa2c chore: Add a missing cleanup step to the coverage script
Looks like `cargo llvm-cov` doesn't clean up the entire `target/llvm-cov-target` directory tree, which means running the coverage script more than once fails as `mv` refuses to overwrite the leftover doctest binaries from a previous run.
2024-12-19 12:36:32 +01:00
Philipp Dresselmann
ec6706ffeb chore(docs): Add a docstring example for uds_send_fd 2024-12-19 11:42:46 +01:00
Paul Spooren
7571670e71 docs(wireguard-broker): add docs and examples (#550) 2024-12-19 09:51:51 +01:00
David Niehues
0d7dd99d96 test(wireguard-broker): Add smoketest and doc-tests for wiregaurd broker 2024-12-19 09:34:40 +01:00
David Niehues
c78a9cb777 docs(wireguard-broker): add docs and examples 2024-12-19 09:34:15 +01:00
Paul Spooren
dd0db53e8b chore(doc): Docs for rosenpass::{config, cli} (#560)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-18 23:11:45 +01:00
Paul Spooren
422acf9891 Docs and unit tests for app_server.rs (#552) 2024-12-18 23:11:25 +01:00
Paul Spooren
877c15a018 chore(docs): minor typo fixes in app_server.rs
Signed-off-by: Paul Spooren <mail@aparcar.org>
2024-12-18 22:47:37 +01:00
Paul Spooren
55d7f8b1c1 Avoid unnecessarily copying the doctest binaries (#558) 2024-12-18 22:44:24 +01:00
Paul Spooren
199ff63a06 Add docstring examples and unit tests for the LengthPrefixEncoder module (#500)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-18 22:01:53 +01:00
Karolin Varner
47b556e317 chore(doc): Docs for rosenpass::{config, cli} 2024-12-18 20:48:12 +01:00
Philipp Dresselmann
f87e2cb31b chore(doc): Fix module descriptions for length_prefix_encoding
There's a more complete module description for the encoder and decoder now. Both versions get appended by rustdoc, which looks wrong and shouldn't be necessary.
2024-12-18 16:24:50 +01:00
Philipp Dresselmann
58e1c8fbff chore(coverage): Add unit tests for the LengthPrefixEncoder
There's some redundancy here with the docstring examples/tests, but that's entirely on purpose:

Unfortunately, it seems that the coverage tool has trouble recognizing calls from within the docstring examples. It's an unstable feature - maybe that's why?

Even with these tests, the tool still doesn't properly detect everything. Regardless, function coverage is 100% when running the coverage tool locally.
2024-12-18 16:24:50 +01:00
Philipp Dresselmann
c89c7d7acf chore(doc): Add docstring examples for the LengthPrefixEncoder 2024-12-18 16:24:50 +01:00
Philipp Dresselmann
a5b876f119 chore(doc): Add a module summary for LengthPrefixEncoder 2024-12-18 16:23:13 +01:00
Philipp Dresselmann
c2f50f47b3 chore(doc): Update docstrings for LengthPrefixEncoder
This is more consistent with the LengthPrefixDecoder documentation.
2024-12-18 16:23:13 +01:00
Paul Spooren
53168dc62d Add documentation, doc-tests and examples to the secret-memory crate. (#531) 2024-12-18 16:18:11 +01:00
David Niehues
2cfe703118 docu(secret-memeory): improve comment in example for Secret 2024-12-18 16:15:35 +01:00
David Niehues
a2d7c3aaa6 chore(secret-memory): fix typos 2024-12-18 16:15:35 +01:00
David Niehues
1aa111570e style(secret-memory): improve style in doc-tests around using the the ?-operator 2024-12-18 16:15:35 +01:00
David Niehues
a91d61f9f0 docs(secret-memory): fix warnings when generating the documentation 2024-12-18 16:15:35 +01:00
David Niehues
ff7827c24e test(fix-doctest): fix doctests where a function si wrapped around a doctest but the function is never called 2024-12-18 16:15:35 +01:00
David Niehues
255e377d29 test(coverage): add unit tests to improve coverage in public.rs and secret.rs 2024-12-18 16:15:35 +01:00
David Niehues
50505d81cc test: fix doctest in alloc/mod.rs to make it work on macos 2024-12-18 16:15:35 +01:00
David Niehues
10484cc6d4 docs(doctests+coverage): add documentation and doctests for all modules of secret-memory except for alloc 2024-12-18 16:15:35 +01:00
David Niehues
d27e602f43 docu(doctest+coverage): add documentation, doc-tests and examples to the alloc module 2024-12-18 16:15:35 +01:00
Philipp Dresselmann
73f6b33dbb chore: Avoid unnecessarily copying the doctest binaries
The doctest binaries can take up ~3GB for a debug build. There's no reason to waste that much disk space and copying them is slower than moving, too. They're only used by `grcov` right now, so they needn't be preserved.
2024-12-18 16:05:34 +01:00
Paul Spooren
a279dfc0b1 docs+doctest(to): Add tests, examples and documentation to the to-crate (#546) 2024-12-18 14:30:38 +01:00
Karolin Varner
caf2f6bfec chore: Remove unused warning in api integration test 2024-12-18 14:28:51 +01:00
Karolin Varner
d398ad369e fix: Disable asserts that rely on timing characteristics during coverage testing 2024-12-18 14:28:35 +01:00
Karolin Varner
00696321ff chore: Final improvements on the to crate API doc 2024-12-18 14:28:24 +01:00
Paul Spooren
d807a1bca7 Add examples and docstring improvements for mio/uds_recv_fd (#551)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-18 12:29:20 +01:00
Karolin Varner
4daf97b2ee style(ciphers): improve style in doc-tests around using the the ?-operator in the ciphers crate (#549) 2024-12-18 11:23:59 +01:00
Karolin Varner
b394e302ab chore(tests): start using unused test output (#547) 2024-12-18 11:22:38 +01:00
Paul Spooren
198bc2d5f2 chore(tests): start using unused test output
Resolve a warning of unused `output` variable.

Fixes: 0745019 docs(cli): Create commented config file

Signed-off-by: Paul Spooren <mail@aparcar.org>
2024-12-18 11:22:16 +01:00
Paul Spooren
fc2f535eae docs(util): add docs and examples for the remaining util crate (#545) 2024-12-18 11:16:00 +01:00
Paul Spooren
302e249f08 docs(constant-time): add docs, examples and safety notices (#544) 2024-12-18 10:58:35 +01:00
dependabot[bot]
d8fe3eba5f build(deps): bump clap_complete from 4.5.38 to 4.5.40
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.38 to 4.5.40.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.38...clap_complete-v4.5.40)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-18 10:29:44 +01:00
Karolin Varner
35519e7baa chore: Documentation and examples for app_server.rs 2024-12-17 18:18:27 +01:00
Karolin Varner
78af5d1dc4 chore: Mark CryptoServer::poll example as ignore
No need to run a test that is in tests/ also during the doctests
2024-12-17 18:14:55 +01:00
David Niehues
61b8b28e86 style(ciphers): improve style in doc-tests around using the the ?-operator in the ciphers crate 2024-12-17 11:57:54 +01:00
Amin Faez
26f77924f8 docs(constant-time): add docs, examples and safety notices 2024-12-17 11:56:33 +01:00
Amin Faez
2e0e2cfa0c docs(util): add docs and examples for the remaining util crate 2024-12-17 11:55:23 +01:00
Karolin Varner
9cc860fdeb Fix doctests where Function is wrapped around the actual test but is never called in cipher traits (#542)
Some checks failed
Nix / Build i686-linux.default (push) Blocked by required conditions
Nix / Build i686-linux.rosenpass (push) Waiting to run
Nix / Build i686-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Run Nix checks on i686-linux (push) Waiting to run
Nix / Build x86_64-darwin.default (push) Blocked by required conditions
Nix / Build x86_64-darwin.release-package (push) Blocked by required conditions
Nix / Build x86_64-darwin.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
2024-12-16 23:30:34 +01:00
Philipp Dresselmann
a537eb3e1b chore(docs): Adjust docstrings for the mio module 2024-12-16 22:29:01 +01:00
Philipp Dresselmann
ea233bf137 chore(docs): Add an example for the UnixListenerExt trait 2024-12-16 22:28:53 +01:00
Philipp Dresselmann
db8796ab40 chore(docs): Add an example for the uds_recv_fd module 2024-12-16 20:54:08 +01:00
David Niehues
0353c82729 docs+doctest(to): Add tests, examples and documentation to the to-crate 2024-12-16 17:47:44 +01:00
David Niehues
ae3fbde0a3 test(fix-doctest): fix doctests where a function is wrapped around a doctest but the function is never called
In the doctests in kem.rs, the actual tests that are run to verify that the KyberKem and the DummyKem actually work
are wrapped inside a function to make use of the ?-operator. However, these functions were never called and thus
the tests weren't really helpful and didn't provide proper coverage.
2024-12-16 17:05:41 +01:00
Philipp Dresselmann
51d4dede15 chore(doc): Add a link to the MIO utils module summary 2024-12-16 17:02:43 +01:00
Karolin Varner
4725a2d628 Merge: fix most broken doc-links (ciphers & cipher-traits) (#543)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-16 16:34:14 +01:00
David Niehues
a6bac74d48 docs(ciphers+cipher-traits):fix most broken doc-links in the ciphers and cipher-traits crates.
Some links in the documentation of the ciphers and cipher-traits were broken or linked to private fields.
This PR fixes most of these occasions and some more warnings in cargo doc.

The reaming issues are links to chacha20poly1305_ietf, that are broken because the feature experiment_libcrux corresponding feature is enabled. Analogously, disabling the feature would lead to broken links to chacha20poly1305_ietf_libcrux.
2024-12-16 16:33:18 +01:00
Karolin Varner
b9a34f4238 protocol.rs docs and unit tests (#537) 2024-12-16 16:31:33 +01:00
Karolin Varner
46e855b266 chore(doc): Documentation, examples & tests for protocol.rs
Co-authored-by: Paul Spooren <mail@aparcar.org>
2024-12-16 16:31:22 +01:00
Karolin Varner
c0b91fd729 fix: Reinstate blanket error handling in event loop
Fixes #534
2024-12-16 16:31:22 +01:00
Karolin Varner
97dff8453d Fix grcov reports not including doctest and branch coverage (#548) 2024-12-16 16:30:55 +01:00
Philipp Dresselmann
a3d4686104 chore(coverage): Fix doctest coverage in the grcov reports
The binary path doesn't contain any doctest executables (i.e., rust_out).

Coverage reports then don't include doctests, presumably because grcov can't map the profdata references to its respective doctest binary.
2024-12-16 15:13:07 +01:00
Philipp Dresselmann
cee0678817 chore(coverage): Fix llvm-cov branch coverage metrics
Without this flag, the generated reports show 0% branch coverage.
2024-12-16 15:13:07 +01:00
Paul Spooren
a996f194c7 docs(util): add docs and examples to the zerocopy module (#532)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-16 11:25:24 +01:00
Paul Spooren
447be89414 docs(util): fix doc reference in decoder.rs (#538) 2024-12-16 09:59:58 +01:00
Amin Faez
ef4f550abc docs(util): fix doc reference in the zerocopy module 2024-12-15 13:05:55 +01:00
Amin Faez
4737cd2b2a docs(util): fix doc reference in decoder.rs
docs(util): add more tests and example to complete coverage
2024-12-15 12:48:47 +01:00
Amin Faez
9336794e4d docs(util): add docs and examples to the zerocopy module 2024-12-14 03:00:27 +01:00
Paul Spooren
096bac6ee5 Add documentation for the rp crate (#528)
Some checks failed
Nix / Build aarch64-linux.rosenpass (push) Has been cancelled
Nix / Build aarch64-linux.rp (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static (push) Has been cancelled
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2024-12-13 12:35:19 +01:00
Karolin Varner
161826979a build(deps): bump serde from 1.0.215 to 1.0.216 (#530)
Some checks are pending
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-13 10:25:20 +01:00
dependabot[bot]
c435b772d2 build(deps): bump serde from 1.0.215 to 1.0.216
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.215 to 1.0.216.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.215...v1.0.216)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 23:45:16 +00:00
David Niehues
8805ef7c38 style: Ensure inline comments start upper case and end with a dot, and fix some overlong lines. 2024-12-12 21:14:02 +01:00
David Niehues
cca02dc8d1 add documentation for the rp crate 2024-12-12 21:14:02 +01:00
Karolin Varner
d4350195eb build(deps): bump rustix from 0.38.41 to 0.38.42 (#524)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-12 18:13:23 +01:00
dependabot[bot]
1c5e4ecf95 build(deps): bump rustix from 0.38.41 to 0.38.42
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.38.41 to 0.38.42.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.38.41...v0.38.42)

---
updated-dependencies:
- dependency-name: rustix
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-12 18:12:41 +01:00
Karolin Varner
b15947b815 Add doc-tests to the cipher-traits crate (#529) 2024-12-12 18:08:00 +01:00
David Niehues
cacbf8535c add eqality test for the shk in the DummyKem 2024-12-12 15:42:56 +01:00
David Niehues
f6d9da4a18 add doc-tests to cipher-traits 2024-12-11 21:16:40 +01:00
David Niehues
68f73e264d add oqs and secret-memory as dev-dependencies to cipher-trait for doc-tests 2024-12-11 21:11:51 +01:00
David Niehues
d5f68dcbd2 fix typo in readme.md 2024-12-11 21:08:40 +01:00
Karolin Varner
96581ed118 Add docs and tests for the decoder module in util::length_prefix_encoding (#526)
Some checks failed
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Build x86_64-linux.whitepaper (push) Has been cancelled
Nix / Run Nix checks on x86_64-linux (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2024-12-11 00:11:57 +01:00
Karolin Varner
553b058759 build(deps): bump libc from 0.2.167 to 0.2.168 (#525) 2024-12-11 00:07:46 +01:00
dependabot[bot]
85286c146f build(deps): bump libc from 0.2.167 to 0.2.168
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.167 to 0.2.168.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.168/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.167...0.2.168)

---
updated-dependencies:
- dependency-name: libc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-11 00:07:31 +01:00
Karolin Varner
0f58b36c5b chore(coverage): Fix missing coverage from API integration tests (#523) 2024-12-11 00:02:34 +01:00
Karolin Varner
737781c8bc chore(coverage): Fix missing coverage from API integration tests
Three changes:
1. We neglected to forward stderr from Rosenpass subprocess two
   in the API setup integration test (driveby fix)
2. Added rudimentary signal handling for program termination
   to rosenpass, specifically for the coverage reporting
3. Apparently std::process::Child::kill() sends SIGKILL and not
   SIGTERM, so our nice new signal handler was never used.
   Switched to a rustix based child reaper.

(2) and (3) where necessary because llvm-cov does not produce coverage
when a subprocess terminates due to a default signal handler.
2024-12-11 00:01:44 +01:00
Karolin Varner
4ea1c76b81 Add documentation for the ciphers crate (#506) 2024-12-10 23:57:56 +01:00
Amin Faez
5251721bcf Add docs and tests for the decoder module in length_prefix_encoding 2024-12-10 16:13:16 +01:00
David Niehues
a789f801ab fix formatting 2024-12-10 12:35:22 +01:00
David Niehues
be06f8adec add tests and documentation for hash_domain.rs 2024-12-10 12:35:22 +01:00
David Niehues
03d3c70e2e document lib.rs and mod.rs, and format documentation for incorrect_hmac_blake2b.rs 2024-12-10 12:35:22 +01:00
David Niehues
94ba99d89b add documentation for hash_domain.rs 2024-12-10 12:35:22 +01:00
David Niehues
667a994253 add documentation for blake2b hmac 2024-12-10 12:35:22 +01:00
David Niehues
9561ea4a47 add documentation for xchacha20polxy1305_ietf.rs and improve documentaion for other implementations for chacha20poly1305 2024-12-10 12:35:22 +01:00
David Niehues
fb641f8568 document chacha20poly1305 as implemented in RustCrypto 2024-12-10 12:35:22 +01:00
David Niehues
6e16956bc7 document chacha20poly1305 as implemented in libcrux 2024-12-10 12:35:22 +01:00
David Niehues
eeb738b649 add documentation and doc-tests for blake2b.rs 2024-12-10 12:35:21 +01:00
Karolin Varner
2d20ad6335 fix: CI issues under Darwin
Some checks failed
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Build x86_64-linux.whitepaper (push) Has been cancelled
Nix / Run Nix checks on x86_64-linux (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2024-12-09 15:35:34 +01:00
Jacek Galowicz
df3d1821c8 Fix build for mac 2024-12-09 15:35:34 +01:00
Jacek Galowicz
6048ebd3d9 rp systemd unit file: introduce and test 2024-12-09 15:35:34 +01:00
Jacek Galowicz
cd7558594f rp: Add exchange-config command
This is similar to `rosenpass exchange`/`rosenpass exchange-config`.
It's however slightly different to the configuration file models the `rp
exchange` command line.
2024-12-09 15:35:34 +01:00
Jacek Galowicz
022cdc4ffa rp: set allowed-ips as routes
Prepare the rp app for a systemd unit file that sets up wireguard
connections.
2024-12-09 15:35:34 +01:00
Jacek Galowicz
06d4e289a5 rp: Add ip parameter to exchange command
Prepare the `rp` app for a systemd unit that sets up a wireguard connection.
2024-12-09 15:35:34 +01:00
Jacek Galowicz
f9dce3fc9a rosenpass systemd unit file: introduce and test 2024-12-09 15:35:34 +01:00
Karolin Varner
d9f3c8fb96 Documentation and unit tests for the rosenpass crate (#520)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / prettier (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-09 07:23:09 +01:00
Karolin Varner
0ea9f1061e chore(doc): rosenpass::api 2024-12-08 23:29:31 +01:00
Karolin Varner
737f0bc9f9 chore(tests): Man page generation integration tests 2024-12-08 23:26:06 +01:00
Karolin Varner
32ebd18107 chore(doc): Docu & tests for rosenpass/src/main.rs 2024-12-08 23:26:06 +01:00
Karolin Varner
f04cff6d57 chore: Remove unused error case InvalidApiMessage 2024-12-08 23:26:06 +01:00
Karolin Varner
2c1a0a7451 chore(doc): document rosenpass/src/lib.rs 2024-12-08 23:26:06 +01:00
Karolin Varner
74fdb44680 chore(doc): Move doc from protocol::protocol into protocol 2024-12-08 00:45:58 +01:00
Karolin Varner
c3adbb7cf3 chore(doc): Documentation for gen-ipc-msg-types binary
It is very basic, because this a developer tool we should refactor anyway.
2024-12-08 00:45:58 +01:00
Karolin Varner
fa583ec6ae chore(doc): Document rosenpass:hash_domains 2024-12-07 17:32:42 +01:00
Karolin Varner
aa76db1e1c chore(rosenpass::msgs): Remove unused fields 2024-12-07 15:26:47 +01:00
Karolin Varner
c5699b5259 chore(doc): Documentation & tests for rosenpass::msgs 2024-12-07 15:26:47 +01:00
Karolin Varner
d3c52fdf64 chore(coverage): Use CodeCov token 2024-12-07 15:26:47 +01:00
Karolin Varner
b18f05ae19 feat(doc): How to format rust code 2024-12-07 15:26:47 +01:00
Karolin Varner
d8839ba341 feat(coverage): Reduce coverage false-negatives using grcov
Previously, we would report some tag style macros such as
`#[repr(packed)]` as being uncovered.

We are now also including doctests in our coverage reports.

Finally, a new script `coverage_report.sh` makes coverage checking
easier.
2024-12-07 15:26:47 +01:00
Karolin Varner
7022a93378 feat(docs): CONTRIBUTING.md with basic info about testing coverage 2024-12-07 15:26:47 +01:00
Karolin Varner
c9da9b8591 Hash based retransmission (#513)
Some checks failed
Nix / Build x86_64-linux.rosenpass-static (push) Has been cancelled
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Build x86_64-linux.whitepaper (push) Has been cancelled
Nix / Run Nix checks on x86_64-linux (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test (ubuntu-latest) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2024-12-07 12:37:01 +01:00
Karolin Varner
b483612cb7 feat(protocol): Hash-based retransmission mechanism
See the updated whitepaper for details.

Fixes: #331
2024-12-07 12:36:40 +01:00
Karolin Varner
a30805f8a0 feat(nix): Dev shell environment with full rust installation
$ nix develop .#fullEnv

This environment contains extra utilities such as the rust language
server.
2024-12-07 12:36:40 +01:00
Karolin Varner
a9b0a90ab5 chore: Update affiliations in whitepaper 2024-12-07 12:36:40 +01:00
Karolin Varner
2bc138e614 chore: Add more info on denial of service mitigation to whitepaper changelog 2024-12-07 12:36:40 +01:00
Karolin Varner
f97781039f build(deps): bump clap from 4.5.22 to 4.5.23 (#519)
Some checks are pending
Nix / Build x86_64-linux.default (push) Blocked by required conditions
Nix / Build x86_64-linux.proof-proverif (push) Blocked by required conditions
Nix / Build x86_64-linux.proverif-patched (push) Waiting to run
Nix / Build x86_64-linux.release-package (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rosenpass (push) Waiting to run
Nix / Build aarch64-linux.rp (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build aarch64-linux.rosenpass-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.rosenpass-static (push) Waiting to run
Nix / Build x86_64-linux.rp-static (push) Waiting to run
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Blocked by required conditions
Nix / Build x86_64-linux.whitepaper (push) Waiting to run
Nix / Run Nix checks on x86_64-linux (push) Waiting to run
Nix / Upload whitepaper x86_64-linux (push) Waiting to run
QC / cargo-doc (push) Waiting to run
QC / cargo-bench (push) Waiting to run
QC / mandoc (push) Waiting to run
QC / cargo-audit (push) Waiting to run
QC / cargo-clippy (push) Waiting to run
QC / prettier (push) Waiting to run
QC / Shellcheck (push) Waiting to run
QC / Rust Format (push) Waiting to run
QC / cargo-test (macos-13) (push) Waiting to run
QC / cargo-test (ubuntu-latest) (push) Waiting to run
QC / cargo-test-nix-devshell-x86_64-linux (push) Waiting to run
QC / cargo-fuzz (push) Waiting to run
QC / codecov (push) Waiting to run
Regressions / multi-peer (push) Waiting to run
Regressions / boot-race (push) Waiting to run
2024-12-06 17:00:00 +01:00
dependabot[bot]
5eda161cf2 build(deps): bump clap from 4.5.22 to 4.5.23
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.22 to 4.5.23.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.22...clap_complete-v4.5.23)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-05 23:53:58 +00:00
Karolin Varner
a473fe6d9b build(deps): bump clap from 4.5.21 to 4.5.22 (#518)
Some checks failed
Nix / Build aarch64-linux.rosenpass (push) Has been cancelled
Nix / Build aarch64-linux.rp (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static (push) Has been cancelled
Nix / Build x86_64-linux.rp-static (push) Has been cancelled
Nix / Upload whitepaper x86_64-linux (push) Has been cancelled
QC / prettier (push) Has been cancelled
QC / Shellcheck (push) Has been cancelled
QC / Rust Format (push) Has been cancelled
QC / cargo-bench (push) Has been cancelled
QC / mandoc (push) Has been cancelled
QC / cargo-audit (push) Has been cancelled
QC / cargo-clippy (push) Has been cancelled
QC / cargo-doc (push) Has been cancelled
QC / cargo-test (macos-13) (push) Has been cancelled
QC / cargo-test-nix-devshell-x86_64-linux (push) Has been cancelled
QC / cargo-fuzz (push) Has been cancelled
QC / codecov (push) Has been cancelled
Regressions / multi-peer (push) Has been cancelled
Regressions / boot-race (push) Has been cancelled
Nix / Build i686-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-darwin.default (push) Has been cancelled
Nix / Build x86_64-darwin.release-package (push) Has been cancelled
Nix / Build x86_64-darwin.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.proof-proverif (push) Has been cancelled
Nix / Build x86_64-linux.release-package (push) Has been cancelled
Nix / Build i686-linux.default (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build aarch64-linux.rosenpass-oci-image (push) Has been cancelled
Nix / Build x86_64-linux.rosenpass-static-oci-image (push) Has been cancelled
2024-12-04 11:40:33 +01:00
185 changed files with 16891 additions and 1673 deletions

1
.dockerignore Symbolic link
View File

@@ -0,0 +1 @@
.gitignore

288
.github/workflows/docker.yaml vendored Normal file
View File

@@ -0,0 +1,288 @@
name: Build Docker Images
# Run this job on all non-pull-request events,
# or if Docker-related files are changed in a pull request.
on:
push:
branches:
- "main"
tags:
- "v*"
pull_request:
paths:
- "docker/Dockerfile"
- ".github/workflows/docker.yaml"
branches:
- "main"
permissions:
contents: read
packages: write
jobs:
# --------------------------------
# 1. BUILD & TEST
# --------------------------------
build-and-test-rp:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build (no push) and Load
id: build
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
# no pushing here, so we can test locally
push: false
# load the built image into the local Docker daemon on the runner
load: true
target: rosenpass
tags: rosenpass:test
platforms: linux/${{ matrix.arch }}
- name: Integration Test - Standalone Key Exchange
run: |
# Create separate workdirs
mkdir -p workdir-server workdir-client
# Create a Docker network
docker network create -d bridge rp
echo "=== GENERATE SERVER KEYS ==="
docker run --rm \
-v $PWD/workdir-server:/workdir \
rosenpass:test gen-keys \
--public-key=workdir/server-public \
--secret-key=workdir/server-secret
echo "=== GENERATE CLIENT KEYS ==="
docker run --rm \
-v $PWD/workdir-client:/workdir \
rosenpass:test gen-keys \
--public-key=workdir/client-public \
--secret-key=workdir/client-secret
echo "=== SHARE PUBLIC KEYS ==="
cp workdir-client/client-public workdir-server/client-public
cp workdir-server/server-public workdir-client/server-public
echo "=== START SERVER CONTAINER ==="
docker run -d --rm \
--name rpserver \
--network rp \
-v $PWD/workdir-server:/workdir \
rosenpass:test exchange \
private-key workdir/server-secret \
public-key workdir/server-public \
listen 0.0.0.0:9999 \
peer public-key workdir/client-public \
outfile workdir/server-sharedkey
# Get the container IP of the server
SERVER_IP=$(docker inspect --format='{{.NetworkSettings.Networks.rp.IPAddress}}' rpserver)
echo "SERVER_IP=$SERVER_IP"
echo "=== START CLIENT CONTAINER ==="
docker run -d --rm \
--name rpclient \
--network rp \
-v $PWD/workdir-client:/workdir \
rosenpass:test exchange \
private-key workdir/client-secret \
public-key workdir/client-public \
peer public-key workdir/server-public \
endpoint ${SERVER_IP}:9999 \
outfile workdir/client-sharedkey
echo "=== COMPARE SHARED KEYS ==="
echo "Waiting up to 30 seconds for the server to generate 'server-sharedkey'..."
for i in $(seq 1 30); do
if [ -f "workdir-server/server-sharedkey" ]; then
echo "server-sharedkey found!"
break
fi
sleep 1
done
sudo cmp workdir-server/server-sharedkey workdir-client/client-sharedkey
echo "Standalone Key Exchange test OK."
# --------------------------------
# 2. PUSH (only if tests pass)
# --------------------------------
docker-image-rp:
needs:
- build-and-test-rp
# Skip if this is not a PR. Then we want to push this image.
if: ${{ github.event_name != 'pull_request' }}
# Use a matrix to build for both AMD64 and ARM64
strategy:
matrix:
arch: [amd64, arm64]
# Switch the runner based on the architecture
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/rp
labels: |
maintainer=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
org.opencontainers.image.authors=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
org.opencontainers.image.title=Rosenpass
org.opencontainers.image.description=The rp command-line integrates Rosenpass and WireGuard to help you create a VPN
org.opencontainers.image.vendor=Rosenpass e.V.
org.opencontainers.image.licenses=MIT OR Apache-2.0
org.opencontainers.image.url=https://rosenpass.eu
org.opencontainers.image.documentation=https://rosenpass.eu/docs/
org.opencontainers.image.source=https://github.com/rosenpass/rosenpass
- name: Log in to registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/rp
target: rp
platforms: linux/${{ matrix.arch }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-rp-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
docker-image-rosenpass:
needs:
- build-and-test-rp
# Skip if this is not a PR. Then we want to push this image.
if: ${{ github.event_name != 'pull_request' }}
# Use a matrix to build for both AMD64 and ARM64
strategy:
matrix:
arch: [amd64, arm64]
# Switch the runner based on the architecture
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/rosenpass
labels: |
maintainer=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
org.opencontainers.image.authors=Karolin Varner <karo@cupdev.net>, wucke13 <wucke13@gmail.com>
org.opencontainers.image.title=Rosenpass
org.opencontainers.image.description=Reference implementation of the protocol rosenpass protocol
org.opencontainers.image.vendor=Rosenpass e.V.
org.opencontainers.image.licenses=MIT OR Apache-2.0
org.opencontainers.image.url=https://rosenpass.eu
org.opencontainers.image.documentation=https://rosenpass.eu/docs/
org.opencontainers.image.source=https://github.com/rosenpass/rosenpass
- name: Log in to registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository_owner }}/rosenpass
target: rosenpass
platforms: linux/${{ matrix.arch }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-rosenpass-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge-digests:
runs-on: ubuntu-latest
needs:
- docker-image-rosenpass
- docker-image-rp
if: ${{ github.event_name != 'pull_request' }}
strategy:
matrix:
target: [rp, rosenpass]
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-${{ matrix.target }}-*
merge-multiple: true
- name: Log in to registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}
tags: |
type=edge,branch=main
type=sha,branch=main
type=semver,pattern={{version}}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/${{ matrix.target }}:${{ steps.meta.outputs.version }}

19
.github/workflows/manual-mac-pr.yaml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: PR Validation on Mac
on:
workflow_dispatch:
permissions:
checks: write
contents: write
concurrency:
group: manual-mac-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
qc:
uses: ./.github/workflows/qc-mac.yaml
permissions:
checks: write
contents: read
nix:
uses: ./.github/workflows/nix-mac.yaml
permissions:
contents: write

114
.github/workflows/nix-mac.yaml vendored Normal file
View File

@@ -0,0 +1,114 @@
name: Nix on Mac
permissions:
contents: write
on:
push:
branches:
- main
workflow_call:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
aarch64-darwin---default:
name: Build aarch64-darwin.default
runs-on:
- warp-macos-13-arm64-6x
needs:
- aarch64-darwin---rosenpass
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.aarch64-darwin.default --print-build-logs
aarch64-darwin---release-package:
name: Build aarch64-darwin.release-package
runs-on:
- warp-macos-13-arm64-6x
needs:
- aarch64-darwin---rosenpass
- aarch64-darwin---rp
- aarch64-darwin---rosenpass-oci-image
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.aarch64-darwin.release-package --print-build-logs
aarch64-darwin---rosenpass:
name: Build aarch64-darwin.rosenpass
runs-on:
- warp-macos-13-arm64-6x
needs: []
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.aarch64-darwin.rosenpass --print-build-logs
aarch64-darwin---rp:
name: Build aarch64-darwin.rp
runs-on:
- warp-macos-13-arm64-6x
needs: []
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.aarch64-darwin.rp --print-build-logs
aarch64-darwin---rosenpass-oci-image:
name: Build aarch64-darwin.rosenpass-oci-image
runs-on:
- warp-macos-13-arm64-6x
needs:
- aarch64-darwin---rosenpass
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.aarch64-darwin.rosenpass-oci-image --print-build-logs
aarch64-darwin---check:
name: Run Nix checks on aarch64-darwin
runs-on:
- warp-macos-13-arm64-6x
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Check
run: nix flake check . --print-build-logs

View File

@@ -15,7 +15,7 @@ jobs:
i686-linux---default:
name: Build i686-linux.default
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs:
- i686-linux---rosenpass
steps:
@@ -32,7 +32,7 @@ jobs:
i686-linux---rosenpass:
name: Build i686-linux.rosenpass
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs: []
steps:
- uses: actions/checkout@v4
@@ -48,7 +48,7 @@ jobs:
i686-linux---rosenpass-oci-image:
name: Build i686-linux.rosenpass-oci-image
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs:
- i686-linux---rosenpass
steps:
@@ -65,107 +65,7 @@ jobs:
i686-linux---check:
name: Run Nix checks on i686-linux
runs-on:
- ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Check
run: nix flake check . --print-build-logs
x86_64-darwin---default:
name: Build x86_64-darwin.default
runs-on:
- macos-13
needs:
- x86_64-darwin---rosenpass
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.default --print-build-logs
x86_64-darwin---release-package:
name: Build x86_64-darwin.release-package
runs-on:
- macos-13
needs:
- x86_64-darwin---rosenpass
- x86_64-darwin---rp
- x86_64-darwin---rosenpass-oci-image
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.release-package --print-build-logs
x86_64-darwin---rosenpass:
name: Build x86_64-darwin.rosenpass
runs-on:
- macos-13
needs: []
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.rosenpass --print-build-logs
x86_64-darwin---rp:
name: Build x86_64-darwin.rp
runs-on:
- macos-13
needs: []
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.rp --print-build-logs
x86_64-darwin---rosenpass-oci-image:
name: Build x86_64-darwin.rosenpass-oci-image
runs-on:
- macos-13
needs:
- x86_64-darwin---rosenpass
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build
run: nix build .#packages.x86_64-darwin.rosenpass-oci-image --print-build-logs
x86_64-darwin---check:
name: Run Nix checks on x86_64-darwin
runs-on:
- macos-13
- ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
@@ -180,7 +80,7 @@ jobs:
x86_64-linux---default:
name: Build x86_64-linux.default
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs:
- x86_64-linux---rosenpass
steps:
@@ -197,7 +97,7 @@ jobs:
x86_64-linux---proof-proverif:
name: Build x86_64-linux.proof-proverif
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs:
- x86_64-linux---proverif-patched
steps:
@@ -214,7 +114,7 @@ jobs:
x86_64-linux---proverif-patched:
name: Build x86_64-linux.proverif-patched
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs: []
steps:
- uses: actions/checkout@v4
@@ -230,7 +130,7 @@ jobs:
x86_64-linux---release-package:
name: Build x86_64-linux.release-package
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs:
- x86_64-linux---rosenpass-static
- x86_64-linux---rosenpass-static-oci-image
@@ -249,7 +149,7 @@ jobs:
# aarch64-linux---release-package:
# name: Build aarch64-linux.release-package
# runs-on:
# - ubuntu-latest
# - ubicloud-standard-2-arm-ubuntu-2204
# needs:
# - aarch64-linux---rosenpass-oci-image
# - aarch64-linux---rosenpass
@@ -273,7 +173,7 @@ jobs:
x86_64-linux---rosenpass:
name: Build x86_64-linux.rosenpass
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs: []
steps:
- uses: actions/checkout@v4
@@ -289,12 +189,12 @@ jobs:
aarch64-linux---rosenpass:
name: Build aarch64-linux.rosenpass
runs-on:
- ubuntu-latest
- ubicloud-standard-2-arm-ubuntu-2204
needs: []
steps:
- run: |
DEBIAN_FRONTEND=noninteractive
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
@@ -310,12 +210,12 @@ jobs:
aarch64-linux---rp:
name: Build aarch64-linux.rp
runs-on:
- ubuntu-latest
- ubicloud-standard-2-arm-ubuntu-2204
needs: []
steps:
- run: |
DEBIAN_FRONTEND=noninteractive
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
@@ -331,7 +231,7 @@ jobs:
x86_64-linux---rosenpass-oci-image:
name: Build x86_64-linux.rosenpass-oci-image
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs:
- x86_64-linux---rosenpass
steps:
@@ -348,13 +248,13 @@ jobs:
aarch64-linux---rosenpass-oci-image:
name: Build aarch64-linux.rosenpass-oci-image
runs-on:
- ubuntu-latest
- ubicloud-standard-2-arm-ubuntu-2204
needs:
- aarch64-linux---rosenpass
steps:
- run: |
DEBIAN_FRONTEND=noninteractive
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi binfmt-support qemu-user-static
sudo apt-get update -q -y && sudo apt-get install -q -y qemu-system-aarch64 qemu-efi-aarch64 binfmt-support qemu-user-static
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
@@ -370,7 +270,7 @@ jobs:
x86_64-linux---rosenpass-static:
name: Build x86_64-linux.rosenpass-static
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs: []
steps:
- uses: actions/checkout@v4
@@ -386,7 +286,7 @@ jobs:
x86_64-linux---rp-static:
name: Build x86_64-linux.rp-static
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs: []
steps:
- uses: actions/checkout@v4
@@ -402,7 +302,7 @@ jobs:
x86_64-linux---rosenpass-static-oci-image:
name: Build x86_64-linux.rosenpass-static-oci-image
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs:
- x86_64-linux---rosenpass-static
steps:
@@ -419,7 +319,7 @@ jobs:
x86_64-linux---whitepaper:
name: Build x86_64-linux.whitepaper
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
needs: []
steps:
- uses: actions/checkout@v4
@@ -435,7 +335,7 @@ jobs:
x86_64-linux---check:
name: Run Nix checks on x86_64-linux
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
@@ -449,7 +349,7 @@ jobs:
run: nix flake check . --print-build-logs
x86_64-linux---whitepaper-upload:
name: Upload whitepaper x86_64-linux
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
if: ${{ github.ref == 'refs/heads/main' }}
steps:
- uses: actions/checkout@v4

32
.github/workflows/qc-mac.yaml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: QC Mac
on:
push:
branches: [main]
workflow_call:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
checks: write
contents: read
jobs:
cargo-test-mac:
runs-on: warp-macos-13-arm64-6x
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# liboqs requires quite a lot of stack memory, thus we adjust
# the default stack size picked for new threads (which is used
# by `cargo test`) to be _big enough_. Setting it to 8 MiB
- run: RUST_MIN_STACK=8388608 cargo test --workspace --all-features

View File

@@ -14,7 +14,7 @@ permissions:
jobs:
prettier:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: actionsx/prettier@v3
@@ -23,7 +23,7 @@ jobs:
shellcheck:
name: Shellcheck
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
@@ -31,14 +31,14 @@ jobs:
rustfmt:
name: Rust Format
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- name: Run Rust Formatting Script
run: bash format_rust_code.sh --mode check
cargo-bench:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -57,7 +57,7 @@ jobs:
mandoc:
name: mandoc
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- name: Install mandoc
run: sudo apt-get install -y mandoc
@@ -66,7 +66,7 @@ jobs:
run: doc/check.sh doc/rp.1
cargo-audit:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: actions-rs/audit-check@v1
@@ -74,7 +74,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
cargo-clippy:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -93,7 +93,7 @@ jobs:
args: --all-features
cargo-doc:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -115,7 +115,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-13]
os: [ubicloud-standard-2-ubuntu-2204, warp-macos-13-arm64-6x]
# - ubuntu is x86-64
# - macos-13 is also x86-64 architecture
steps:
@@ -136,7 +136,7 @@ jobs:
cargo-test-nix-devshell-x86_64-linux:
runs-on:
- ubuntu-latest
- ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -158,7 +158,8 @@ jobs:
- run: nix develop --command cargo test --workspace --all-features
cargo-fuzz:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
env:
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
@@ -173,7 +174,7 @@ jobs:
- name: Install nightly toolchain
run: |
rustup toolchain install nightly
rustup default nightly
rustup override nightly
- name: Install cargo-fuzz
run: cargo install cargo-fuzz
- name: Run fuzzing
@@ -191,22 +192,23 @@ jobs:
cargo fuzz run fuzz_vec_secret_alloc_memfdsec_mallocfb -- -max_total_time=5
codecov:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- run: rustup default nightly
- run: rustup component add llvm-tools-preview
- run: |
cargo install cargo-llvm-cov || true
cargo llvm-cov \
--workspace\
--all-features \
--lcov \
--output-path coverage.lcov
cargo install grcov || true
./coverage_report.sh
# If using tarapulin
#- run: cargo install cargo-tarpaulin
#- run: cargo tarpaulin --out Xml
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
files: ./coverage.lcov
files: ./target/grcov/lcov
verbose: true
env:
RUSTUP_TOOLCHAIN: 1.81
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -14,7 +14,7 @@ permissions:
jobs:
multi-peer:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- run: cargo build --bin rosenpass --release
@@ -25,7 +25,7 @@ jobs:
[ $(ls -1 output/ate/out | wc -l) -eq 100 ]
boot-race:
runs-on: ubuntu-latest
runs-on: ubicloud-standard-2-ubuntu-2204
steps:
- uses: actions/checkout@v4
- run: cargo build --bin rosenpass --release

View File

@@ -13,8 +13,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
@@ -34,8 +32,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v15
with:
name: rosenpass
@@ -69,3 +65,24 @@ jobs:
draft: ${{ contains(github.ref_name, 'rc') }}
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
files: result/*
linux-packages:
name: Build and upload DEB and RPM packages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
- uses: cachix/cachix-action@v15
with:
name: rosenpass
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Build DEB & RPM package
run: |
mkdir packages
for f in $(nix build .#package-deb .#package-rpm --print-out-paths); do cp "$f" "packages/${f#*-}"; done
- name: Release
uses: softprops/action-gh-release@v2
with:
draft: ${{ contains(github.ref_name, 'rc') }}
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
files: |
packages/*

91
.github/workflows/supply-chain.yml vendored Normal file
View File

@@ -0,0 +1,91 @@
name: Supply-Chain
on:
pull_request:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
cargo-deny:
name: Deny dependencies with vulnerabilities or incompatible licenses
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
cargo-supply-chain:
name: Supply Chain Report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cache/cargo-supply-chain/
key: cargo-supply-chain-cache
- uses: actions/cache@v4
with:
path: ${{ runner.tool_cache }}/cargo-supply-chain
key: cargo-supply-chain-bin
- name: Add the tool cache directory to the search path
run: echo "${{ runner.tool_cache }}/cargo-supply-chain/bin" >> $GITHUB_PATH
- name: Ensure that the tool cache is populated with the cargo-supply-chain binary
run: cargo install --root ${{ runner.tool_cache }}/cargo-supply-chain cargo-supply-chain
- name: Update data for cargo-supply-chain
run: cargo supply-chain update
- name: Generate cargo-supply-chain report about publishers
run: cargo supply-chain publishers
- name: Generate cargo-supply-chain report about crates
run: cargo supply-chain crates
# The setup for cargo-vet follows the recommendations in the cargo-vet documentation: https://mozilla.github.io/cargo-vet/configuring-ci.html
cargo-vet:
name: Vet Dependencies
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
key: cargo-vet-cache
- name: Install stable toolchain # Since we are running/compiling cargo-vet, we should rely on the stable toolchain.
run: |
rustup toolchain install stable
rustup default stable
- uses: actions/cache@v4
with:
path: ${{ runner.tool_cache }}/cargo-vet
key: cargo-vet-bin
- name: Add the tool cache directory to the search path
run: echo "${{ runner.tool_cache }}/cargo-vet/bin" >> $GITHUB_PATH
- name: Ensure that the tool cache is populated with the cargo-vet binary
run: cargo install --root ${{ runner.tool_cache }}/cargo-vet cargo-vet
- name: Regenerate vet exemptions for dependabot PRs
if: github.actor == 'dependabot[bot]' # Run only for Dependabot PRs
run: cargo vet regenerate exemptions
- name: Check for changes in case of dependabot PR
if: github.actor == 'dependabot[bot]' # Run only for Dependabot PRs
run: git diff --exit-code || echo "Changes detected, committing..."
- name: Commit and push changes for dependabot PRs
if: success() && github.actor == 'dependabot[bot]'
run: |
git fetch origin ${{ github.head_ref }}
git switch ${{ github.head_ref }}
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions@github.com"
git add supply-chain/*
git commit -m "Regenerate cargo vet exemptions"
git push origin ${{ github.head_ref }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Invoke cargo-vet
run: cargo vet --locked

1
.gitignore vendored
View File

@@ -25,3 +25,4 @@ _markdown_*
.vscode
/output
.nixos-test-history

View File

@@ -1,38 +1,41 @@
**Making a new Release of Rosenpass — Cooking Recipe**
# Contributing to Rosenpass
If you have to change a file, do what it takes to get the change as commit on the main branch, then **start from step 0**.
If any other issue occurs
## Common operations
0. Make sure you are in the root directory of the project
- `cd "$(git rev-parse --show-toplevel)"`
1. Make sure you locally checked out the head of the main branch
- `git stash --include-untracked && git checkout main && git pull`
2. Make sure all tests pass
- `cargo test --workspace --all-features`
3. Make sure the current version in `rosenpass/Cargo.toml` matches that in the [last release on GitHub](https://github.com/rosenpass/rosenpass/releases)
- Only normal releases count, release candidates and draft releases can be ignored
4. Pick the kind of release that you want to make (`major`, `minor`, `patch`, `rc`, ...)
- See `cargo release --help` for more information on the available release types
- Pick `rc` if in doubt
5. Try to release a new version
- `cargo release rc --package rosenpass`
- An issue was reported? Go fix it, start again with step 0!
6. Actually make the release
- `cargo release rc --package rosenpass --execute`
- Tentatively wait for any interactions, such as entering ssh keys etc.
- You may be asked for your ssh key multiple times!
### Apply code formatting
**Frequently Asked Questions (FAQ)**
Format rust code:
- You have untracked files, which `cargo release` complains about?
- `git stash --include-untracked`
- You cannot push to crates.io because you are not logged in?
- Follow the steps displayed in [`cargo login`](https://doc.rust-lang.org/cargo/commands/cargo-login.html)
- How is the release page added to [GitHub Releases](https://github.com/rosenpass/rosenpass/releases) itself?
- Our CI Pipeline will create the release, once `cargo release` pushed the new version tag to the repo. The new release should pop up almost immediately in [GitHub Releases](https://github.com/rosenpass/rosenpass/releases) after the [Actions/Release](https://github.com/rosenpass/rosenpass/actions/workflows/release.yaml) pipeline started.
- No new release pops up in the `Release` sidebar element on the [main page](https://github.com/rosenpass/rosenpass)
- Did you push a `rc` release? This view only shows non-draft release, but `rc` releases are considered as draft. See [Releases](https://github.com/rosenpass/rosenpass/releases) page to see all (including draft!) releases.
- The release page was created on GitHub, but there are no assets/artifacts other than the source code tar ball/zip?
- The artifacts are generated and pushed automatically to the release, but this takes some time (a couple of minutes). You can check the respective CI pipeline: [Actions/Release](https://github.com/rosenpass/rosenpass/actions/workflows/release.yaml), which should start immediately after `cargo release` pushed the new release tag to the repo. The release artifacts only are added later to the release, once all jobs in bespoke pipeline finished.
- How are the release artifacts generated, and what are they?
- The release artifacts are built using one Nix derivation per platform, `nix build .#release-package`. It contains both statically linked versions of `rosenpass` itself and OCI container images.
```bash
cargo fmt
```
Format rust code in markdown files:
```bash
./format_rust_code.sh --mode fix
```
### Spawn a development environment with nix
```bash
nix develop .#fullEnv
```
You need to [install this nix package manager](https://wiki.archlinux.org/title/Nix) first.
### Run our test
Make sure to increase the stack size available; some of our cryptography operations require a lot of stack memory.
```bash
RUST_MIN_STACK=8388608 cargo test --workspace --all-features
```
### Generate coverage reports
Keep in mind that many of Rosenpass' tests are doctests, so to get an accurate read on our code coverage, you have to include doctests:
```bash
./coverage_report.sh
```

883
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -47,33 +47,38 @@ memsec = { git = "https://github.com/rosenpass/memsec.git", rev = "aceb9baee8aec
rand = "0.8.5"
typenum = "1.17.0"
log = { version = "0.4.22" }
clap = { version = "4.5.22", features = ["derive"] }
clap = { version = "4.5.23", features = ["derive"] }
clap_mangen = "0.2.24"
clap_complete = "4.5.38"
serde = { version = "1.0.215", features = ["derive"] }
clap_complete = "4.5.40"
serde = { version = "1.0.217", features = ["derive"] }
arbitrary = { version = "1.4.1", features = ["derive"] }
anyhow = { version = "1.0.94", features = ["backtrace", "std"] }
anyhow = { version = "1.0.95", features = ["backtrace", "std"] }
mio = { version = "1.0.3", features = ["net", "os-poll"] }
oqs-sys = { version = "0.9.1", default-features = false, features = [
'classic_mceliece',
'kyber',
] }
blake2 = "0.10.6"
sha3 = "0.10.8"
chacha20poly1305 = { version = "0.10.1", default-features = false, features = [
"std",
"heapless",
] }
zerocopy = { version = "0.7.35", features = ["derive"] }
home = "0.5.9"
home = "=0.5.9" # 5.11 requires rustc 1.81
derive_builder = "0.20.1"
tokio = { version = "1.42", features = ["macros", "rt-multi-thread"] }
postcard = { version = "1.1.1", features = ["alloc"] }
libcrux = { version = "0.0.2-pre.2" }
libcrux-chacha20poly1305 = { version = "0.0.2-beta.3" }
libcrux-ml-kem = { version = "0.0.2-beta.3" }
libcrux-blake2 = { git = "https://github.com/cryspen/libcrux.git", rev = "10ce653e9476"}
hex-literal = { version = "0.4.1" }
hex = { version = "0.4.3" }
heck = { version = "0.5.0" }
libc = { version = "0.2" }
uds = { git = "https://github.com/rosenpass/uds" }
signal-hook = "0.3.17"
#Dev dependencies
serial_test = "3.2.0"
@@ -81,7 +86,7 @@ tempfile = "3"
stacker = "0.1.17"
libfuzzer-sys = "0.4"
test_bin = "0.4.0"
criterion = "0.4.0"
criterion = "0.5.1"
allocator-api2-tests = "0.2.15"
procspawn = { version = "1.0.1", features = ["test-support"] }
@@ -89,4 +94,7 @@ procspawn = { version = "1.0.1", features = ["test-support"] }
#Broker dependencies (might need cleanup or changes)
wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
command-fds = "0.2.3"
rustix = { version = "0.38.41", features = ["net", "fs"] }
rustix = { version = "0.38.42", features = ["net", "fs", "process"] }
futures = "0.3"
futures-util = "0.3"
x25519-dalek = "2"

View File

@@ -8,5 +8,13 @@ description = "Rosenpass internal traits for cryptographic primitives"
homepage = "https://rosenpass.eu/"
repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
rust-version = "1.77"
[dependencies]
thiserror = { workspace = true }
rosenpass-to = { workspace = true }
[dev-dependencies]
rosenpass-oqs = { workspace = true }
rosenpass-secret-memory = { workspace = true }
anyhow = { workspace = true }

View File

@@ -2,4 +2,4 @@
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.
This is an internal library; no guarantee is made about its API at this point in time.

View File

@@ -0,0 +1,137 @@
//! This module contains the traits for all the cryptographic algorithms used throughout Rosenpass.
//! These traits are marker traits that signal intent. They can also be used for trait objects.
/// Constants and trait for the Incorrect HMAC over Blake2b, with 256 key and hash length.
pub mod keyed_hash_incorrect_hmac_blake2b {
use crate::primitives::keyed_hash::*;
// These constants describe how they are used here, not what the algorithm defines.
/// The key length used in [`KeyedHashIncorrectHmacBlake2b`].
pub const KEY_LEN: usize = 32;
/// The hash length used in [`KeyedHashIncorrectHmacBlake2b`].
pub const HASH_LEN: usize = 32;
/// A [`KeyedHash`] that is an incorrect HMAC over Blake2 (a custom Rosenpass construction)
pub trait KeyedHashIncorrectHmacBlake2b: KeyedHash<KEY_LEN, HASH_LEN> {}
}
/// Constants and trait for Blake2b, with 256 key and hash length.
pub mod keyed_hash_blake2b {
use crate::primitives::keyed_hash::*;
// These constants describe how they are used here, not what the algorithm defines.
/// The key length used in [`KeyedHashBlake2b`].
pub const KEY_LEN: usize = 32;
/// The hash length used in [`KeyedHashBlake2b`].
pub const HASH_LEN: usize = 32;
/// A [`KeyedHash`] that is Blake2b
pub trait KeyedHashBlake2b: KeyedHash<KEY_LEN, HASH_LEN> {}
}
/// Constants and trait for SHAKE256, with 256 key and hash length.
pub mod keyed_hash_shake256 {
use crate::primitives::keyed_hash::*;
// These constants describe how they are used here, not what the algorithm defines.
/// The key length used in [`KeyedHashShake256`].
pub const KEY_LEN: usize = 32;
/// The hash length used in [`KeyedHashShake256`].
pub const HASH_LEN: usize = 32;
/// A [`KeyedHash`] that is SHAKE256.
pub trait KeyedHashShake256: KeyedHash<KEY_LEN, HASH_LEN> {}
}
/// Constants and trait for the ChaCha20Poly1305 AEAD
pub mod aead_chacha20poly1305 {
use crate::primitives::aead::*;
// See https://datatracker.ietf.org/doc/html/rfc7539#section-2.8
/// The key length used in [`AeadChaCha20Poly1305`].
pub const KEY_LEN: usize = 32;
/// The nonce length used in [`AeadChaCha20Poly1305`].
pub const NONCE_LEN: usize = 12;
/// The tag length used in [`AeadChaCha20Poly1305`].
pub const TAG_LEN: usize = 16;
/// An [`Aead`] that is ChaCha20Poly1305.
pub trait AeadChaCha20Poly1305: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {}
}
/// Constants and trait for the XChaCha20Poly1305 AEAD (i.e. ChaCha20Poly1305 with extended nonce
/// lengths)
pub mod aead_xchacha20poly1305 {
use crate::primitives::aead::*;
// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03
/// The key length used in [`AeadXChaCha20Poly1305`].
pub const KEY_LEN: usize = 32;
/// The nonce length used in [`AeadXChaCha20Poly1305`].
pub const NONCE_LEN: usize = 24;
/// The tag length used in [`AeadXChaCha20Poly1305`].
pub const TAG_LEN: usize = 16;
/// An [`Aead`] that is XChaCha20Poly1305.
pub trait AeadXChaCha20Poly1305: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {}
}
/// Constants and trait for the Kyber512 KEM
pub mod kem_kyber512 {
use crate::primitives::kem::*;
// page 39 of https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf
// (which is ml-kem instead of kyber, but it's the same)
/// The secret key length used in [`KemKyber512`].
pub const SK_LEN: usize = 1632;
/// The public key length used in [`KemKyber512`].
pub const PK_LEN: usize = 800;
/// The ciphertext length used in [`KemKyber512`].
pub const CT_LEN: usize = 768;
/// The shared key length used in [`KemKyber512`].
pub const SHK_LEN: usize = 32;
/// A [`Kem`] that is Kyber512.
pub trait KemKyber512: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> {}
}
/// Constants and trait for the Classic McEliece 460896 KEM
pub mod kem_classic_mceliece460896 {
use crate::primitives::kem::*;
// page 6 of https://classic.mceliece.org/mceliece-impl-20221023.pdf
/// The secret key length used in [`KemClassicMceliece460896`].
pub const SK_LEN: usize = 13608;
/// The public key length used in [`KemClassicMceliece460896`].
pub const PK_LEN: usize = 524160;
/// The ciphertext length used in [`KemClassicMceliece460896`].
pub const CT_LEN: usize = 156;
/// The shared key length used in [`KemClassicMceliece460896`].
pub const SHK_LEN: usize = 32;
/// A [`Kem`] that is ClassicMceliece460896.
pub trait KemClassicMceliece460896: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> {}
}
pub use aead_chacha20poly1305::AeadChaCha20Poly1305;
pub use aead_xchacha20poly1305::AeadXChaCha20Poly1305;
pub use kem_classic_mceliece460896::KemClassicMceliece460896;
pub use kem_kyber512::KemKyber512;
pub use keyed_hash_blake2b::KeyedHashBlake2b;
pub use keyed_hash_incorrect_hmac_blake2b::KeyedHashIncorrectHmacBlake2b;
pub use keyed_hash_shake256::KeyedHashShake256;

View File

@@ -5,10 +5,128 @@
//!
//! 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].
//!
//! The [Kem] Trait describes the basic API offered by a Key Encapsulation
//! Mechanism. Two implementations for it are provided:
//! [Kyber512](../../rosenpass_oqs/kyber_512/enum.Kyber512.html) and
//! [ClassicMceliece460896](../../rosenpass_oqs/classic_mceliece_460896/enum.ClassicMceliece460896.html).
//!
//! An example where Alice generates a keypair and gives her public key to Bob, for Bob to
//! encapsulate a symmetric key and Alice to decapsulate it would look as follows.
//! In the example, we are using Kyber512, but any KEM that correctly implements the [Kem]
//! trait could be used as well.
//!```rust
//! use rosenpass_cipher_traits::Kem;
//! use rosenpass_oqs::Kyber512;
//! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret};
//!
//! type MyKem = Kyber512;
//! secret_policy_use_only_malloc_secrets();
//! let mut alice_sk: Secret<{ MyKem::SK_LEN }> = Secret::zero();
//! let mut alice_pk: [u8; MyKem::PK_LEN] = [0; MyKem::PK_LEN];
//! MyKem::keygen(alice_sk.secret_mut(), &mut alice_pk)?;
//!
//! let mut bob_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
//! let mut bob_ct: [u8; MyKem::CT_LEN] = [0; MyKem::CT_LEN];
//! MyKem::encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?;
//!
//! let mut alice_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
//! MyKem::decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?;
//!
//! # assert_eq!(alice_shk.secret(), bob_shk.secret());
//! # Ok::<(), anyhow::Error>(())
//!```
//!
//! Implementing the [Kem]-trait for a KEM is easy. Mostly, you must format the KEM's
//! keys, and ciphertext as `u8` slices. Below, we provide an example for how the trait can
//! be implemented using a **HORRIBLY INSECURE** DummyKem that only uses static values for keys
//! and ciphertexts as an example.
//!```rust
//!# use rosenpass_cipher_traits::Kem;
//!
//! struct DummyKem {}
//! impl Kem for DummyKem {
//!
//! // For this DummyKem, using String for errors is sufficient.
//! type Error = String;
//!
//! // For this DummyKem, we will use a single `u8` for everything
//! const SK_LEN: usize = 1;
//! const PK_LEN: usize = 1;
//! const CT_LEN: usize = 1;
//! const SHK_LEN: usize = 1;
//!
//! fn keygen(sk: &mut [u8], pk: &mut [u8]) -> Result<(), Self::Error> {
//! if sk.len() != Self::SK_LEN {
//! return Err("sk does not have the correct length!".to_string());
//! }
//! if pk.len() != Self::PK_LEN {
//! return Err("pk does not have the correct length!".to_string());
//! }
//! sk[0] = 42;
//! pk[0] = 21;
//! Ok(())
//! }
//!
//! fn encaps(shk: &mut [u8], ct: &mut [u8], pk: &[u8]) -> Result<(), Self::Error> {
//! if pk.len() != Self::PK_LEN {
//! return Err("pk does not have the correct length!".to_string());
//! }
//! if ct.len() != Self::CT_LEN {
//! return Err("ct does not have the correct length!".to_string());
//! }
//! if shk.len() != Self::SHK_LEN {
//! return Err("shk does not have the correct length!".to_string());
//! }
//! if pk[0] != 21 {
//! return Err("Invalid public key!".to_string());
//! }
//! ct[0] = 7;
//! shk[0] = 17;
//! Ok(())
//! }
//!
//! fn decaps(shk: &mut [u8], sk: &[u8], ct: &[u8]) -> Result<(), Self::Error> {
//! if sk.len() != Self::SK_LEN {
//! return Err("sk does not have the correct length!".to_string());
//! }
//! if ct.len() != Self::CT_LEN {
//! return Err("ct does not have the correct length!".to_string());
//! }
//! if shk.len() != Self::SHK_LEN {
//! return Err("shk does not have the correct length!".to_string());
//! }
//! if sk[0] != 42 {
//! return Err("Invalid public key!".to_string());
//! }
//! if ct[0] != 7 {
//! return Err("Invalid ciphertext!".to_string());
//! }
//! shk[0] = 17;
//! Ok(())
//! }
//! }
//! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret};
//! #
//! # type MyKem = DummyKem;
//! # secret_policy_use_only_malloc_secrets();
//! # let mut alice_sk: Secret<{ MyKem::SK_LEN }> = Secret::zero();
//! # let mut alice_pk: [u8; MyKem::PK_LEN] = [0; MyKem::PK_LEN];
//! # MyKem::keygen(alice_sk.secret_mut(), &mut alice_pk)?;
//!
//! # let mut bob_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
//! # let mut bob_ct: [u8; MyKem::CT_LEN] = [0; MyKem::CT_LEN];
//! # MyKem::encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?;
//! #
//! # let mut alice_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
//! # MyKem::decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?;
//! #
//! # assert_eq!(alice_shk.secret(), bob_shk.secret());
//! #
//! # Ok::<(), String>(())
//!```
//!
/// Key Encapsulation Mechanism
///

View File

@@ -1,2 +1,5 @@
mod kem;
pub use kem::Kem;
//! This trait contains traits, constants and wrappers that provid= the interface between Rosenpass
//! as a consumer of cryptographic libraries and the implementations of cryptographic algorithms.
pub mod algorithms;
pub mod primitives;

View File

@@ -0,0 +1,10 @@
//! Traits for cryptographic primitives used in Rosenpass, specifically KEM, AEAD and keyed
//! hashing.
pub(crate) mod aead;
pub(crate) mod kem;
pub(crate) mod keyed_hash;
pub use aead::{Aead, AeadWithNonceInCiphertext, Error as AeadError};
pub use kem::{Error as KemError, Kem};
pub use keyed_hash::*;

View File

@@ -0,0 +1,175 @@
use rosenpass_to::{ops::copy_slice, To as _};
use thiserror::Error;
/// Models authenticated encryption with assiciated data (AEAD) functionality.
///
/// The methods of this trait take a `&self` argument as a receiver. This has two reasons:
/// 1. It makes type inference a lot smoother
/// 2. It allows to use the functionality through a trait object or having an enum that has
/// variants for multiple options (like e.g. the `KeyedHash` enum in `rosenpass-ciphers`).
///
/// Since the caller needs an instance of the type to use the functionality, implementors are
/// adviced to implement the [`Default`] trait where possible.
///
/// Example for encrypting a message with a specific [`Aead`] instance:
/// ```
/// use rosenpass_cipher_traits::primitives::Aead;
///
/// const KEY_LEN: usize = 32;
/// const NONCE_LEN: usize = 12;
/// const TAG_LEN: usize = 16;
///
/// fn encrypt_message_given_an_aead<AeadImpl>(
/// aead: &AeadImpl,
/// msg: &str,
/// nonce: &[u8; NONCE_LEN],
/// encrypted: &mut [u8]
/// ) where AeadImpl: Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {
/// let key = [0u8; KEY_LEN]; // This is not a secure key!
/// let ad = b""; // we don't need associated data here
/// aead.encrypt(encrypted, &key, nonce, ad, msg.as_bytes()).unwrap();
/// }
/// ```
///
/// If only the type (but no instance) is available, then we can still encrypt, as long as the type
/// also is [`Default`]:
/// ```
/// use rosenpass_cipher_traits::primitives::Aead;
///
/// const KEY_LEN: usize = 32;
/// const NONCE_LEN: usize = 12;
/// const TAG_LEN: usize = 16;
///
/// fn encrypt_message_without_aead<AeadImpl>(
/// msg: &str,
/// nonce: &[u8; NONCE_LEN],
/// encrypted: &mut [u8]
/// ) where AeadImpl: Default + Aead<KEY_LEN, NONCE_LEN, TAG_LEN> {
/// let key = [0u8; KEY_LEN]; // This is not a secure key!
/// let ad = b""; // we don't need associated data here
/// AeadImpl::default().encrypt(encrypted, &key, nonce, ad, msg.as_bytes()).unwrap();
/// }
/// ```
pub trait Aead<const KEY_LEN: usize, const NONCE_LEN: usize, const TAG_LEN: usize> {
const KEY_LEN: usize = KEY_LEN;
const NONCE_LEN: usize = NONCE_LEN;
const TAG_LEN: usize = TAG_LEN;
/// Encrypts `plaintext` using the given `key` and `nonce`, taking into account the additional
/// data `ad` and writes the result into `ciphertext`.
///
/// `ciphertext` must be exactly `TAG_LEN` longer than `plaintext`.
fn encrypt(
&self,
ciphertext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
plaintext: &[u8],
) -> Result<(), Error>;
/// Decrypts `ciphertexttext` using the given `key` and `nonce`, taking into account the additional
/// data `ad` and writes the result into `plaintext`.
///
/// `ciphertext` must be exactly `TAG_LEN` longer than `plaintext`.
fn decrypt(
&self,
plaintext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
ciphertext: &[u8],
) -> Result<(), Error>;
}
/// Provides an AEAD API where the nonce is part of the ciphertext.
///
/// The old xaead API had the ciphertext begin with the `nonce`. In order to not having to change
/// the calling code too much, we add a wrapper trait that provides this API and implement it for
/// all AEAD.
pub trait AeadWithNonceInCiphertext<
const KEY_LEN: usize,
const NONCE_LEN: usize,
const TAG_LEN: usize,
>: Aead<KEY_LEN, NONCE_LEN, TAG_LEN>
{
/// Encrypts `plaintext` using the given `key` and `nonce`, taking into account the additional
/// data `ad` and writes the result into `ciphertext`.
///
/// `ciphertext` must be exactly `TAG_LEN` + `NONCE_LEN` longer than `plaintext`.
fn encrypt_with_nonce_in_ctxt(
&self,
ciphertext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
plaintext: &[u8],
) -> Result<(), Error> {
// The comparison looks complicated, but we need to do it this way to prevent
// over/underflows.
if ciphertext.len() < NONCE_LEN + TAG_LEN
|| ciphertext.len() - TAG_LEN - NONCE_LEN < plaintext.len()
{
return Err(Error::InvalidLengths);
}
let (n, rest) = ciphertext.split_at_mut(NONCE_LEN);
copy_slice(nonce).to(n);
self.encrypt(rest, key, nonce, ad, plaintext)
}
/// Decrypts `ciphertexttext` using the given `key` and `nonce`, taking into account the additional
/// data `ad` and writes the result into `plaintext`.
///
/// `ciphertext` must be exactly `TAG_LEN` + `NONCE_LEN` longer than `plaintext`.
fn decrypt_with_nonce_in_ctxt(
&self,
plaintext: &mut [u8],
key: &[u8; KEY_LEN],
ad: &[u8],
ciphertext: &[u8],
) -> Result<(), Error> {
// The comparison looks complicated, but we need to do it this way to prevent
// over/underflows.
if ciphertext.len() < NONCE_LEN + TAG_LEN
|| ciphertext.len() - TAG_LEN - NONCE_LEN < plaintext.len()
{
return Err(Error::InvalidLengths);
}
let (nonce, rest) = ciphertext.split_at(NONCE_LEN);
// We know this should be the right length (we just split it), and everything else would be
// very unexpected.
let nonce = nonce.try_into().map_err(|_| Error::InternalError)?;
self.decrypt(plaintext, key, nonce, ad, rest)
}
}
impl<
const KEY_LEN: usize,
const NONCE_LEN: usize,
const TAG_LEN: usize,
T: Aead<KEY_LEN, NONCE_LEN, TAG_LEN>,
> AeadWithNonceInCiphertext<KEY_LEN, NONCE_LEN, TAG_LEN> for T
{
}
/// The error returned by AEAD operations
#[derive(Debug, Error)]
pub enum Error {
/// An internal error occurred. This should never be happen and indicates an error in the
/// AEAD implementation.
#[error("internal error")]
InternalError,
/// Could not decrypt a message because the message is not a valid ciphertext for the given
/// key.
#[error("decryption error")]
DecryptError,
/// The provided buffers have the wrong lengths.
#[error("buffers have invalid length")]
InvalidLengths,
}

View File

@@ -0,0 +1,212 @@
//! 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:
//! [Kyber512](../../rosenpass_oqs/kyber_512/enum.Kyber512.html) and
//! [ClassicMceliece460896](../../rosenpass_oqs/classic_mceliece_460896/enum.ClassicMceliece460896.html).
//!
//! An example where Alice generates a keypair and gives her public key to Bob, for Bob to
//! encapsulate a symmetric key and Alice to decapsulate it would look as follows.
//! In the example, we are using Kyber512, but any KEM that correctly implements the [Kem]
//! trait could be used as well.
//!```rust
//! use rosenpass_cipher_traits::primitives::Kem;
//! use rosenpass_oqs::Kyber512;
//! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret};
//!
//! type MyKem = Kyber512;
//! secret_policy_use_only_malloc_secrets();
//! let mut alice_sk: Secret<{ MyKem::SK_LEN }> = Secret::zero();
//! let mut alice_pk: [u8; MyKem::PK_LEN] = [0; MyKem::PK_LEN];
//! MyKem::default().keygen(alice_sk.secret_mut(), &mut alice_pk)?;
//!
//! let mut bob_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
//! let mut bob_ct: [u8; MyKem::CT_LEN] = [0; MyKem::CT_LEN];
//! MyKem::default().encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?;
//!
//! let mut alice_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
//! MyKem::default().decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?;
//!
//! # assert_eq!(alice_shk.secret(), bob_shk.secret());
//! # Ok::<(), anyhow::Error>(())
//!```
//!
//! Implementing the [Kem]-trait for a KEM is easy. Mostly, you must format the KEM's
//! keys, and ciphertext as `u8` slices. Below, we provide an example for how the trait can
//! be implemented using a **HORRIBLY INSECURE** DummyKem that only uses static values for keys
//! and ciphertexts as an example.
//!```rust
//!# use rosenpass_cipher_traits::primitives::{Kem, KemError as Error};
//!
//! struct DummyKem {}
//! impl Kem<1,1,1,1> for DummyKem {
//!
//! // For this DummyKem, we will use a single `u8` for everything
//! const SK_LEN: usize = 1;
//! const PK_LEN: usize = 1;
//! const CT_LEN: usize = 1;
//! const SHK_LEN: usize = 1;
//!
//! fn keygen(&self, sk: &mut [u8;1], pk: &mut [u8;1]) -> Result<(), Error> {
//! sk[0] = 42;
//! pk[0] = 21;
//! Ok(())
//! }
//!
//! fn encaps(&self, shk: &mut [u8;1], ct: &mut [u8;1], pk: &[u8;1]) -> Result<(), Error> {
//! if pk[0] != 21 {
//! return Err(Error::InvalidArgument);
//! }
//! ct[0] = 7;
//! shk[0] = 17;
//! Ok(())
//! }
//!
//! fn decaps(&self, shk: &mut [u8;1 ], sk: &[u8;1], ct: &[u8;1]) -> Result<(), Error> {
//! if sk[0] != 42 {
//! return Err(Error::InvalidArgument);
//! }
//! if ct[0] != 7 {
//! return Err(Error::InvalidArgument);
//! }
//! shk[0] = 17;
//! Ok(())
//! }
//! }
//!
//! impl Default for DummyKem {
//! fn default() -> Self {
//! Self{}
//! }
//! }
//! # use rosenpass_secret_memory::{secret_policy_use_only_malloc_secrets, Secret};
//! #
//! # type MyKem = DummyKem;
//! # secret_policy_use_only_malloc_secrets();
//! # let mut alice_sk: Secret<{ MyKem::SK_LEN }> = Secret::zero();
//! # let mut alice_pk: [u8; MyKem::PK_LEN] = [0; MyKem::PK_LEN];
//! # MyKem::default().keygen(alice_sk.secret_mut(), &mut alice_pk)?;
//!
//! # let mut bob_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
//! # let mut bob_ct: [u8; MyKem::CT_LEN] = [0; MyKem::CT_LEN];
//! # MyKem::default().encaps(bob_shk.secret_mut(), &mut bob_ct, &mut alice_pk)?;
//! #
//! # let mut alice_shk: Secret<{ MyKem::SHK_LEN }> = Secret::zero();
//! # MyKem::default().decaps(alice_shk.secret_mut(), alice_sk.secret_mut(), &mut bob_ct)?;
//! #
//! # assert_eq!(alice_shk.secret(), bob_shk.secret());
//! #
//! # Ok::<(), Error>(())
//!```
//!
use thiserror::Error;
/// Key Encapsulation Mechanism
///
/// The KEM interface defines three operations: Key generation, key encapsulation and key
/// decapsulation. The parameters are made available as associated constants for convenience.
///
/// The methods of this trait take a `&self` argument as a receiver. This has two reasons:
/// 1. It makes type inference a lot smoother
/// 2. It allows to use the functionality through a trait object or having an enum that has
/// variants for multiple options (like e.g. the `KeyedHash` enum in `rosenpass-ciphers`).
///
/// Since the caller needs an instance of the type to use the functionality, implementors are
/// adviced to implement the [`Default`] trait where possible.
///
/// Example for encrypting a message with a specific [`Kem`] instance:
/// ```
/// use rosenpass_cipher_traits::primitives::Kem;
///
/// const SK_LEN: usize = 1632;
/// const PK_LEN: usize = 800;
/// const CT_LEN: usize = 768;
/// const SHK_LEN: usize = 32;
///
/// fn encaps_given_a_kem<KemImpl>(
/// kem: &KemImpl,
/// pk: &[u8; PK_LEN],
/// ct: &mut [u8; CT_LEN]
/// ) -> [u8; SHK_LEN] where KemImpl: Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN>{
/// let mut shk = [0u8; SHK_LEN];
/// kem.encaps(&mut shk, ct, pk).unwrap();
/// shk
/// }
/// ```
///
/// If only the type (but no instance) is available, then we can still use the trait, as long as
/// the type also is [`Default`]:
/// ```
/// use rosenpass_cipher_traits::primitives::Kem;
///
/// const SK_LEN: usize = 1632;
/// const PK_LEN: usize = 800;
/// const CT_LEN: usize = 768;
/// const SHK_LEN: usize = 32;
///
/// fn encaps_without_kem<KemImpl>(
/// pk: &[u8; PK_LEN],
/// ct: &mut [u8; CT_LEN]
/// ) -> [u8; SHK_LEN]
/// where KemImpl: Default + Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> {
/// let mut shk = [0u8; SHK_LEN];
/// KemImpl::default().encaps(&mut shk, ct, pk).unwrap();
/// shk
/// }
/// ```
pub trait Kem<const SK_LEN: usize, const PK_LEN: usize, const CT_LEN: usize, const SHK_LEN: usize> {
/// The length of the secret (decapsulation) key.
const SK_LEN: usize = SK_LEN;
/// The length of the public (encapsulation) key.
const PK_LEN: usize = PK_LEN;
/// The length of the ciphertext.
const CT_LEN: usize = CT_LEN;
/// The legnth of the resulting shared key.
const SHK_LEN: usize = SHK_LEN;
/// Generate a keypair consisting of secret key (`sk`) and public key (`pk`)
///
/// `keygen() -> sk, pk`
fn keygen(&self, sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), 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(
&self,
shk: &mut [u8; SHK_LEN],
ct: &mut [u8; CT_LEN],
pk: &[u8; PK_LEN],
) -> Result<(), Error>;
/// From a secret key (`sk`) and a cipher text (`ct`) derive a shared key
/// (`shk`)
///
/// `decaps(sk, ct) -> shk`
fn decaps(
&self,
shk: &mut [u8; SHK_LEN],
sk: &[u8; SK_LEN],
ct: &[u8; CT_LEN],
) -> Result<(), Error>;
}
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid argument")]
InvalidArgument,
#[error("internal error")]
InternalError,
}

View File

@@ -0,0 +1,159 @@
use std::marker::PhantomData;
/// Models a keyed hash function using an associated function (i.e. without `&self` receiver).
pub trait KeyedHash<const KEY_LEN: usize, const HASH_LEN: usize> {
/// The error type used to signal what went wrong.
type Error;
/// Performs a keyed hash using `key` and `data` and writes the output to `out`
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error>;
}
/// Models a keyed hash function using a method (i.e. with a `&self` receiver).
///
/// This makes type inference easier, but also requires having a [`KeyedHashInstance`] value,
/// instead of just the [`KeyedHash`] type.
pub trait KeyedHashInstance<const KEY_LEN: usize, const HASH_LEN: usize> {
/// The error type used to signal what went wrong.
type Error;
/// Performs a keyed hash using `key` and `data` and writes the output to `out`
fn keyed_hash(
&self,
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error>;
}
/// This is a helper to allow for type parameter inference when calling functions
/// that need a [KeyedHash].
///
/// Really just binds the [KeyedHash] trait to a dummy variable, so the type of this dummy variable
/// can be used for type inference. Less typing work.
#[derive(Debug, PartialEq, Eq)]
pub struct InferKeyedHash<Static, const KEY_LEN: usize, const HASH_LEN: usize>
where
Static: KeyedHash<KEY_LEN, HASH_LEN>,
{
pub _phantom_keyed_hasher: PhantomData<*const Static>,
}
impl<Static, const KEY_LEN: usize, const HASH_LEN: usize> InferKeyedHash<Static, KEY_LEN, HASH_LEN>
where
Static: KeyedHash<KEY_LEN, HASH_LEN>,
{
pub const KEY_LEN: usize = KEY_LEN;
pub const HASH_LEN: usize = HASH_LEN;
pub const fn new() -> Self {
Self {
_phantom_keyed_hasher: PhantomData,
}
}
/// This just forwards to [KeyedHash::keyed_hash] of the type parameter `Static`
fn keyed_hash_internal<'a>(
&self,
key: &'a [u8; KEY_LEN],
data: &'a [u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Static::Error> {
Static::keyed_hash(key, data, out)
}
/// Returns the key length of the keyed hash function.
pub const fn key_len(self) -> usize {
Self::KEY_LEN
}
/// Returns the hash length of the keyed hash function.
pub const fn hash_len(self) -> usize {
Self::HASH_LEN
}
}
impl<const KEY_LEN: usize, const HASH_LEN: usize, Static: KeyedHash<KEY_LEN, HASH_LEN>>
KeyedHashInstance<KEY_LEN, HASH_LEN> for InferKeyedHash<Static, KEY_LEN, HASH_LEN>
{
type Error = Static::Error;
fn keyed_hash(
&self,
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Static::Error> {
self.keyed_hash_internal(key, data, out)
}
}
// Helper traits /////////////////////////////////////////////
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Default
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
where
Static: KeyedHash<KEY_LEN, OUT_LEN>,
{
fn default() -> Self {
Self::new()
}
}
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Clone
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
where
Static: KeyedHash<KEY_LEN, OUT_LEN>,
{
fn clone(&self) -> Self {
*self
}
}
impl<Static, const KEY_LEN: usize, const OUT_LEN: usize> Copy
for InferKeyedHash<Static, KEY_LEN, OUT_LEN>
where
Static: KeyedHash<KEY_LEN, OUT_LEN>,
{
}
use rosenpass_to::{with_destination, To};
/// Extends the [`KeyedHash`] trait with a [`To`]-flavoured function.
pub trait KeyedHashTo<const KEY_LEN: usize, const HASH_LEN: usize>:
KeyedHash<KEY_LEN, HASH_LEN>
{
fn keyed_hash_to(
key: &[u8; KEY_LEN],
data: &[u8],
) -> impl To<[u8; HASH_LEN], Result<(), Self::Error>> {
with_destination(|out| Self::keyed_hash(key, data, out))
}
}
impl<const KEY_LEN: usize, const HASH_LEN: usize, T: KeyedHash<KEY_LEN, HASH_LEN>>
KeyedHashTo<KEY_LEN, HASH_LEN> for T
{
}
/// Extends the [`KeyedHashInstance`] trait with a [`To`]-flavoured function.
pub trait KeyedHashInstanceTo<const KEY_LEN: usize, const HASH_LEN: usize>:
KeyedHashInstance<KEY_LEN, HASH_LEN>
{
fn keyed_hash_to(
&self,
key: &[u8; KEY_LEN],
data: &[u8],
) -> impl To<[u8; HASH_LEN], Result<(), Self::Error>> {
with_destination(|out| self.keyed_hash(key, data, out))
}
}
impl<const KEY_LEN: usize, const HASH_LEN: usize, T: KeyedHashInstance<KEY_LEN, HASH_LEN>>
KeyedHashInstanceTo<KEY_LEN, HASH_LEN> for T
{
}

View File

@@ -8,9 +8,22 @@ description = "Rosenpass internal ciphers and other cryptographic primitives use
homepage = "https://rosenpass.eu/"
repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
rust-version = "1.77"
[features]
experiment_libcrux = ["dep:libcrux"]
experiment_libcrux_all = [
"experiment_libcrux_blake2",
"experiment_libcrux_chachapoly",
"experiment_libcrux_chachapoly_test",
"experiment_libcrux_kyber",
]
experiment_libcrux_blake2 = ["dep:libcrux-blake2", "dep:thiserror"]
experiment_libcrux_chachapoly = ["dep:libcrux-chacha20poly1305"]
experiment_libcrux_chachapoly_test = [
"experiment_libcrux_chachapoly",
"dep:libcrux",
]
experiment_libcrux_kyber = ["dep:libcrux-ml-kem", "dep:rand"]
[dependencies]
anyhow = { workspace = true }
@@ -19,8 +32,21 @@ rosenpass-constant-time = { workspace = true }
rosenpass-secret-memory = { workspace = true }
rosenpass-oqs = { workspace = true }
rosenpass-util = { workspace = true }
rosenpass-cipher-traits = { workspace = true }
static_assertions = { workspace = true }
zeroize = { workspace = true }
chacha20poly1305 = { workspace = true }
blake2 = { workspace = true }
sha3 = { workspace = true }
rand = { workspace = true, optional = true }
thiserror = { workspace = true, optional = true }
libcrux-chacha20poly1305 = { workspace = true, optional = true }
libcrux-blake2 = { workspace = true, optional = true }
libcrux-ml-kem = { workspace = true, optional = true, features = ["kyber"] }
# this one is only used in testing, so it requires the `experiment_libcrux_chachapoly_test` feature.
libcrux = { workspace = true, optional = true }
[dev-dependencies]
rand = { workspace = true }

View File

@@ -1,109 +1,209 @@
//!
//!```rust
//! # use rosenpass_ciphers::hash_domain::{HashDomain, HashDomainNamespace, SecretHashDomain, SecretHashDomainNamespace};
//! use rosenpass_ciphers::KeyedHash;
//! use rosenpass_secret_memory::Secret;
//! # rosenpass_secret_memory::secret_policy_use_only_malloc_secrets();
//!
//! const PROTOCOL_IDENTIFIER: &str = "MY_PROTOCOL:IDENTIFIER";
//! // create use once hash domain for the protocol identifier
//! let mut hash_domain = HashDomain::zero(KeyedHash::keyed_shake256());
//! hash_domain = hash_domain.mix(PROTOCOL_IDENTIFIER.as_bytes())?;
//! // upgrade to reusable hash domain
//! let hash_domain_namespace: HashDomainNamespace = hash_domain.dup();
//! // derive new key
//! let key_identifier = "my_key_identifier";
//! let key = hash_domain_namespace.mix(key_identifier.as_bytes())?.into_value();
//! // derive a new key based on a secret
//! const MY_SECRET_LEN: usize = 21;
//! let my_secret_bytes = "my super duper secret".as_bytes();
//! let my_secret: Secret<21> = Secret::from_slice("my super duper secret".as_bytes());
//! let secret_hash_domain: SecretHashDomain = hash_domain_namespace.mix_secret(my_secret)?;
//! // derive a new key based on the secret key
//! let new_key_identifier = "my_new_key_identifier".as_bytes();
//! let new_key = secret_hash_domain.mix(new_key_identifier)?.into_secret();
//!
//! # Ok::<(), anyhow::Error>(())
//!```
//!
use anyhow::Result;
use rosenpass_secret_memory::Secret;
use rosenpass_to::To;
use rosenpass_to::To as _;
use crate::subtle::incorrect_hmac_blake2b as hash;
pub use crate::{KeyedHash, KEY_LEN};
pub use hash::KEY_LEN;
use rosenpass_cipher_traits::primitives::KeyedHashInstanceTo;
// TODO Use a proper Dec interface
/// A use-once hash domain for a specified key that can be used directly.
/// The key must consist of [KEY_LEN] many bytes. If the key must remain secret,
/// use [SecretHashDomain] instead.
#[derive(Clone, Debug)]
pub struct HashDomain([u8; KEY_LEN]);
pub struct HashDomain([u8; KEY_LEN], KeyedHash);
/// A reusable hash domain for a namespace identified by the key.
/// The key must consist of [KEY_LEN] many bytes. If the key must remain secret,
/// use [SecretHashDomainNamespace] instead.
#[derive(Clone, Debug)]
pub struct HashDomainNamespace([u8; KEY_LEN]);
pub struct HashDomainNamespace([u8; KEY_LEN], KeyedHash);
/// A use-once hash domain for a specified key that can be used directly
/// by wrapping it in [Secret]. The key must consist of [KEY_LEN] many bytes.
#[derive(Clone, Debug)]
pub struct SecretHashDomain(Secret<KEY_LEN>);
pub struct SecretHashDomain(Secret<KEY_LEN>, KeyedHash);
/// A reusable secure hash domain for a namespace identified by the key and that keeps the key secure
/// by wrapping it in [Secret]. The key must consist of [KEY_LEN] many bytes.
#[derive(Clone, Debug)]
pub struct SecretHashDomainNamespace(Secret<KEY_LEN>);
pub struct SecretHashDomainNamespace(Secret<KEY_LEN>, KeyedHash);
impl HashDomain {
pub fn zero() -> Self {
Self([0u8; KEY_LEN])
/// Creates a nw [HashDomain] initialized with a all-zeros key.
pub fn zero(choice: KeyedHash) -> Self {
Self([0u8; KEY_LEN], choice)
}
/// Turns this [HashDomain] into a [HashDomainNamespace], keeping the key.
pub fn dup(self) -> HashDomainNamespace {
HashDomainNamespace(self.0)
HashDomainNamespace(self.0, self.1)
}
/// Turns this [HashDomain] into a [SecretHashDomain] by wrapping the key into a [Secret]
/// and creating a new [SecretHashDomain] from it.
pub fn turn_secret(self) -> SecretHashDomain {
SecretHashDomain(Secret::from_slice(&self.0))
SecretHashDomain(Secret::from_slice(&self.0), self.1)
}
// TODO: Protocol! Use domain separation to ensure that
/// Creates a new [HashDomain] by mixing in a new key `v`. Specifically,
/// it evaluates [hash::hash] with this HashDomain's key as the key and `v`
/// as the `data` and uses the result as the key for the new [HashDomain].
///
pub fn mix(self, v: &[u8]) -> Result<Self> {
Ok(Self(hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?))
let mut new_key: [u8; KEY_LEN] = [0u8; KEY_LEN];
self.1.keyed_hash_to(&self.0, v).to(&mut new_key)?;
Ok(Self(new_key, self.1))
}
/// Creates a new [SecretHashDomain] by mixing in a new key `v`
/// by calling [SecretHashDomain::invoke_primitive] with this
/// [HashDomain]'s key as `k` and `v` as `d`.
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretHashDomain> {
SecretHashDomain::invoke_primitive(&self.0, v.secret())
SecretHashDomain::invoke_primitive(&self.0, v.secret(), self.1)
}
/// Gets the key of this [HashDomain].
pub fn into_value(self) -> [u8; KEY_LEN] {
self.0
}
}
impl HashDomainNamespace {
/// Creates a new [HashDomain] by mixing in a new key `v`. Specifically,
/// it evaluates [hash::hash] with the key of this HashDomainNamespace key as the key and `v`
/// as the `data` and uses the result as the key for the new [HashDomain].
pub fn mix(&self, v: &[u8]) -> Result<HashDomain> {
Ok(HashDomain(
hash::hash(&self.0, v).collect::<[u8; KEY_LEN]>()?,
))
let mut new_key: [u8; KEY_LEN] = [0u8; KEY_LEN];
self.1.keyed_hash_to(&self.0, v).to(&mut new_key)?;
Ok(HashDomain(new_key, self.1.clone()))
}
/// Creates a new [SecretHashDomain] by mixing in a new key `v`
/// by calling [SecretHashDomain::invoke_primitive] with the key of this
/// [HashDomainNamespace] as `k` and `v` as `d`.
///
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretHashDomain> {
SecretHashDomain::invoke_primitive(&self.0, v.secret())
SecretHashDomain::invoke_primitive(&self.0, v.secret(), self.1.clone())
}
}
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())?;
/// Create a new [SecretHashDomain] with the given key `k` and data `d` by calling
/// [hash::hash] with `k` as the `key` and `d` s the `data`, and using the result
/// as the content for the new [SecretHashDomain].
/// Both `k` and `d` have to be exactly [KEY_LEN] bytes in length.
/// TODO: docu
pub fn invoke_primitive(
k: &[u8],
d: &[u8],
hash_choice: KeyedHash,
) -> Result<SecretHashDomain> {
let mut new_secret_key = Secret::zero();
hash_choice
.keyed_hash_to(k.try_into()?, d)
.to(new_secret_key.secret_mut())?;
let r = SecretHashDomain(new_secret_key, hash_choice);
Ok(r)
}
pub fn zero() -> Self {
Self(Secret::zero())
/// Creates a new [SecretHashDomain] that is initialized with an all zeros key.
pub fn zero(hash_choice: KeyedHash) -> Self {
Self(Secret::zero(), hash_choice)
}
/// Turns this [SecretHashDomain] into a [SecretHashDomainNamespace].
pub fn dup(self) -> SecretHashDomainNamespace {
SecretHashDomainNamespace(self.0)
SecretHashDomainNamespace(self.0, self.1)
}
pub fn danger_from_secret(k: Secret<KEY_LEN>) -> Self {
Self(k)
/// Creates a new [SecretHashDomain] from a [Secret] `k`.
///
/// It requires that `k` consist of exactly [KEY_LEN] bytes.
pub fn danger_from_secret(k: Secret<KEY_LEN>, hash_choice: KeyedHash) -> Self {
Self(k, hash_choice)
}
/// Creates a new [SecretHashDomain] by mixing in a new key `v`. Specifically,
/// it evaluates [hash::hash] with this [SecretHashDomain]'s key as the key and `v`
/// as the `data` and uses the result as the key for the new [SecretHashDomain].
///
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
pub fn mix(self, v: &[u8]) -> Result<SecretHashDomain> {
Self::invoke_primitive(self.0.secret(), v)
Self::invoke_primitive(self.0.secret(), v, self.1)
}
/// Creates a new [SecretHashDomain] by mixing in a new key `v`
/// by calling [SecretHashDomain::invoke_primitive] with the key of this
/// [HashDomainNamespace] as `k` and `v` as `d`.
///
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
pub fn mix_secret<const N: usize>(self, v: Secret<N>) -> Result<SecretHashDomain> {
Self::invoke_primitive(self.0.secret(), v.secret())
Self::invoke_primitive(self.0.secret(), v.secret(), self.1)
}
/// Get the secret key data from this [SecretHashDomain].
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 {
/// Creates a new [SecretHashDomain] by mixing in a new key `v`. Specifically,
/// it evaluates [hash::hash] with the key of this HashDomainNamespace key as the key and `v`
/// as the `data` and uses the result as the key for the new [HashDomain].
///
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
pub fn mix(&self, v: &[u8]) -> Result<SecretHashDomain> {
SecretHashDomain::invoke_primitive(self.0.secret(), v)
SecretHashDomain::invoke_primitive(self.0.secret(), v, self.1.clone())
}
/// Creates a new [SecretHashDomain] by mixing in a new key `v`
/// by calling [SecretHashDomain::invoke_primitive] with the key of this
/// [HashDomainNamespace] as `k` and `v` as `d`.
///
/// It requires that `v` consists of exactly [KEY_LEN] many bytes.
pub fn mix_secret<const N: usize>(&self, v: Secret<N>) -> Result<SecretHashDomain> {
SecretHashDomain::invoke_primitive(self.0.secret(), v.secret())
SecretHashDomain::invoke_primitive(self.0.secret(), v.secret(), self.1.clone())
}
// 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
/// Get the secret key data from this [SecretHashDomain].
pub fn danger_into_secret(self) -> Secret<KEY_LEN> {
self.0
}
pub fn keyed_hash(&self) -> &KeyedHash {
&self.1
}
}

View File

@@ -1,32 +1,46 @@
use rosenpass_cipher_traits::primitives::Aead as AeadTrait;
use static_assertions::const_assert;
pub mod subtle;
/// All keyed primitives in this crate use 32 byte keys
pub const KEY_LEN: usize = 32;
const_assert!(KEY_LEN == aead::KEY_LEN);
const_assert!(KEY_LEN == xaead::KEY_LEN);
const_assert!(KEY_LEN == Aead::KEY_LEN);
const_assert!(KEY_LEN == XAead::KEY_LEN);
const_assert!(KEY_LEN == hash_domain::KEY_LEN);
/// Authenticated encryption with associated data
pub mod aead {
#[cfg(not(feature = "experiment_libcrux"))]
pub use crate::subtle::chacha20poly1305_ietf::{decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN};
#[cfg(feature = "experiment_libcrux")]
pub use crate::subtle::chacha20poly1305_ietf_libcrux::{
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN,
};
}
/// Keyed hashing
///
/// This should only be used for implementation details; anything with relevance
/// to the cryptographic protocol should use the facilities in [hash_domain], (though
/// hash domain uses this module internally)
pub use crate::subtle::keyed_hash::KeyedHash;
/// Authenticated encryption with associated data with a constant nonce
pub mod xaead {
pub use crate::subtle::xchacha20poly1305_ietf::{
decrypt, encrypt, KEY_LEN, NONCE_LEN, TAG_LEN,
};
}
/// Authenticated encryption with associated data (AEAD)
/// Chacha20poly1305 is used.
#[cfg(feature = "experiment_libcrux_chachapoly")]
pub use subtle::libcrux::chacha20poly1305_ietf::ChaCha20Poly1305 as Aead;
/// Authenticated encryption with associated data (AEAD)
/// Chacha20poly1305 is used.
#[cfg(not(feature = "experiment_libcrux_chachapoly"))]
pub use crate::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305 as Aead;
/// Authenticated encryption with associated data with a extended-length nonce (XAEAD)
/// XChacha20poly1305 is used.
pub use crate::subtle::rust_crypto::xchacha20poly1305_ietf::XChaCha20Poly1305 as XAead;
/// Use Classic-McEcliece-460986 as the Static KEM.
///
/// See [rosenpass_oqs::ClassicMceliece460896] for more details.
pub use rosenpass_oqs::ClassicMceliece460896 as StaticKem;
/// Use Kyber-512 as the Static KEM
///
/// See [rosenpass_oqs::Kyber512] for more details.
#[cfg(not(feature = "experiment_libcrux_kyber"))]
pub use rosenpass_oqs::Kyber512 as EphemeralKem;
#[cfg(feature = "experiment_libcrux_kyber")]
pub use subtle::libcrux::kyber512::Kyber512 as EphemeralKem;
pub mod hash_domain;
pub mod kem {
pub use rosenpass_oqs::ClassicMceliece460896 as StaticKem;
pub use rosenpass_oqs::Kyber512 as EphemeralKem;
}

View File

@@ -1,42 +0,0 @@
use zeroize::Zeroizing;
use blake2::digest::crypto_common::generic_array::GenericArray;
use blake2::digest::crypto_common::typenum::U32;
use blake2::digest::crypto_common::KeySizeUser;
use blake2::digest::{FixedOutput, Mac, OutputSizeUser};
use blake2::Blake2bMac;
use rosenpass_to::{ops::copy_slice, with_destination, To};
use rosenpass_util::typenum2const;
type Impl = Blake2bMac<U32>;
type KeyLen = <Impl as KeySizeUser>::KeySize;
type OutLen = <Impl as OutputSizeUser>::OutputSize;
const KEY_LEN: usize = typenum2const! { KeyLen };
const OUT_LEN: usize = typenum2const! { OutLen };
pub const KEY_MIN: usize = KEY_LEN;
pub const KEY_MAX: usize = KEY_LEN;
pub const OUT_MIN: usize = OUT_LEN;
pub const OUT_MAX: usize = OUT_LEN;
#[inline]
pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<()>> + 'a {
with_destination(|out: &mut [u8]| {
let mut h = Impl::new_from_slice(key)?;
h.update(data);
// Jesus christ, blake2 crate, your usage of GenericArray might be nice and fancy
// but it introduces a ton of complexity. This cost me half an hour just to figure
// out the right way to use the imports while allowing for zeroization.
// An API based on slices might actually be simpler.
let mut tmp = Zeroizing::new([0u8; OUT_LEN]);
let tmp = GenericArray::from_mut_slice(tmp.as_mut());
h.finalize_into(tmp);
copy_slice(tmp.as_ref()).to(out);
Ok(())
})
}

View File

@@ -1,43 +0,0 @@
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use rosenpass_util::typenum2const;
use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::ChaCha20Poly1305 as AeadImpl;
use chacha20poly1305::{AeadCore, AeadInPlace, KeyInit, KeySizeUser};
pub const KEY_LEN: usize = typenum2const! { <AeadImpl as KeySizeUser>::KeySize };
pub const TAG_LEN: usize = typenum2const! { <AeadImpl as AeadCore>::TagSize };
pub const NONCE_LEN: usize = typenum2const! { <AeadImpl as AeadCore>::NonceSize };
#[inline]
pub fn encrypt(
ciphertext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
plaintext: &[u8],
) -> anyhow::Result<()> {
let nonce = GenericArray::from_slice(nonce);
let (ct, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
copy_slice(plaintext).to(ct);
let mac_value = AeadImpl::new_from_slice(key)?.encrypt_in_place_detached(nonce, ad, ct)?;
copy_slice(&mac_value[..]).to(mac);
Ok(())
}
#[inline]
pub fn decrypt(
plaintext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<()> {
let nonce = GenericArray::from_slice(nonce);
let (ct, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
let tag = GenericArray::from_slice(mac);
copy_slice(ct).to(plaintext);
AeadImpl::new_from_slice(key)?.decrypt_in_place_detached(nonce, ad, plaintext, tag)?;
Ok(())
}

View File

@@ -1,60 +0,0 @@
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use zeroize::Zeroize;
pub const KEY_LEN: usize = 32; // Grrrr! Libcrux, please provide me these constants.
pub const TAG_LEN: usize = 16;
pub const NONCE_LEN: usize = 12;
#[inline]
pub fn encrypt(
ciphertext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
plaintext: &[u8],
) -> anyhow::Result<()> {
let (ciphertext, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
use libcrux::aead as C;
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
let crux_iv = C::Iv(nonce.try_into().unwrap());
copy_slice(plaintext).to(ciphertext);
let crux_tag = libcrux::aead::encrypt(&crux_key, ciphertext, crux_iv, ad).unwrap();
copy_slice(crux_tag.as_ref()).to(mac);
match crux_key {
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
_ => panic!(),
}
Ok(())
}
#[inline]
pub fn decrypt(
plaintext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<()> {
let (ciphertext, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
use libcrux::aead as C;
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
let crux_iv = C::Iv(nonce.try_into().unwrap());
let crux_tag = C::Tag::from_slice(mac).unwrap();
copy_slice(ciphertext).to(plaintext);
libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag).unwrap();
match crux_key {
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
_ => panic!(),
}
Ok(())
}

View File

@@ -0,0 +1,79 @@
use rosenpass_cipher_traits::{
algorithms::KeyedHashIncorrectHmacBlake2b,
primitives::{InferKeyedHash, KeyedHash, KeyedHashTo},
};
use rosenpass_constant_time::xor;
use rosenpass_to::{ops::copy_slice, To};
use zeroize::Zeroizing;
#[cfg(not(feature = "experiment_libcrux_blake2"))]
use crate::subtle::rust_crypto::blake2b::Blake2b;
#[cfg(not(feature = "experiment_libcrux_blake2"))]
use anyhow::Error;
#[cfg(feature = "experiment_libcrux_blake2")]
use crate::subtle::libcrux::blake2b::{Blake2b, Error};
/// The key length, 32 bytes or 256 bits.
pub const KEY_LEN: usize = 32;
/// The hash length, 32 bytes or 256 bits.
pub const HASH_LEN: usize = 32;
/// This is a woefully incorrect implementation of hmac_blake2b.
/// See <https://github.com/rosenpass/rosenpass/issues/68#issuecomment-1563612222>
///
/// It accepts 32 byte keys, exclusively.
///
/// This will be replaced, likely by Kekkac at some point soon.
/// <https://github.com/rosenpass/rosenpass/pull/145>
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::custom::incorrect_hmac_blake2b::IncorrectHmacBlake2bCore;
/// use rosenpass_cipher_traits::primitives::KeyedHashTo;
/// use rosenpass_to::To;
/// let key: [u8; 32] = [0; 32];
/// let data: [u8; 32] = [255; 32];
/// // buffer for the hash output
/// let mut hash_data: [u8; 32] = [0u8; 32];
///
/// assert!(IncorrectHmacBlake2bCore::keyed_hash_to(&key, &data).to(&mut hash_data).is_ok(), "Hashing has to return OK result");
/// # let expected_hash: &[u8] = &[5, 152, 135, 141, 151, 106, 147, 8, 220, 95, 38, 66, 29, 33, 3,
/// 104, 250, 114, 131, 119, 27, 56, 59, 44, 11, 67, 230, 113, 112, 20, 80, 103];
/// # assert_eq!(hash_data, expected_hash);
///```
///
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IncorrectHmacBlake2bCore;
impl KeyedHash<KEY_LEN, HASH_LEN> for IncorrectHmacBlake2bCore {
type Error = Error;
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error> {
const IPAD: [u8; KEY_LEN] = [0x36u8; KEY_LEN];
const OPAD: [u8; KEY_LEN] = [0x5Cu8; KEY_LEN];
type Key = Zeroizing<[u8; KEY_LEN]>;
let mut tmp_key = Key::default();
copy_slice(key).to(tmp_key.as_mut());
xor(&IPAD).to(tmp_key.as_mut());
let mut outer_data = Key::default();
Blake2b::keyed_hash_to(&tmp_key, data).to(&mut outer_data)?;
copy_slice(key).to(tmp_key.as_mut());
xor(&OPAD).to(tmp_key.as_mut());
Blake2b::keyed_hash_to(&tmp_key, outer_data.as_ref()).to(out)?;
Ok(())
}
}
pub type IncorrectHmacBlake2b = InferKeyedHash<IncorrectHmacBlake2bCore, KEY_LEN, HASH_LEN>;
impl KeyedHashIncorrectHmacBlake2b for IncorrectHmacBlake2bCore {}

View File

@@ -0,0 +1,3 @@
//! Own implementations of custom algorithms
pub mod incorrect_hmac_blake2b;

View File

@@ -1,46 +0,0 @@
use anyhow::ensure;
use zeroize::Zeroizing;
use rosenpass_constant_time::xor;
use rosenpass_to::{ops::copy_slice, with_destination, To};
use crate::subtle::blake2b;
pub const KEY_LEN: usize = 32;
pub const KEY_MIN: usize = KEY_LEN;
pub const KEY_MAX: usize = KEY_LEN;
pub const OUT_MIN: usize = blake2b::OUT_MIN;
pub const OUT_MAX: usize = blake2b::OUT_MAX;
/// This is a woefully incorrect implementation of hmac_blake2b.
/// See <https://github.com/rosenpass/rosenpass/issues/68#issuecomment-1563612222>
///
/// It accepts 32 byte keys, exclusively.
///
/// This will be replaced, likely by Kekkac at some point soon.
/// <https://github.com/rosenpass/rosenpass/pull/145>
#[inline]
pub fn hash<'a>(key: &'a [u8], data: &'a [u8]) -> impl To<[u8], anyhow::Result<()>> + 'a {
const IPAD: [u8; KEY_LEN] = [0x36u8; KEY_LEN];
const OPAD: [u8; KEY_LEN] = [0x5Cu8; KEY_LEN];
with_destination(|out: &mut [u8]| {
// Not bothering with padding; the implementation
// uses appropriately sized keys.
ensure!(key.len() == KEY_LEN);
type Key = Zeroizing<[u8; KEY_LEN]>;
let mut tmp_key = Key::default();
copy_slice(key).to(tmp_key.as_mut());
xor(&IPAD).to(tmp_key.as_mut());
let mut outer_data = Key::default();
blake2b::hash(tmp_key.as_ref(), data).to(outer_data.as_mut())?;
copy_slice(key).to(tmp_key.as_mut());
xor(&OPAD).to(tmp_key.as_mut());
blake2b::hash(tmp_key.as_ref(), outer_data.as_ref()).to(out)?;
Ok(())
})
}

View File

@@ -0,0 +1,65 @@
//! This module provides types that enabling choosing the keyed hash building block to be used at
//! runtime (using enums) instead of at compile time (using generics).
use anyhow::Result;
use rosenpass_cipher_traits::primitives::KeyedHashInstance;
use std::fmt::Display;
use crate::subtle::{
custom::incorrect_hmac_blake2b::IncorrectHmacBlake2b, rust_crypto::keyed_shake256::SHAKE256_32,
};
/// Length of symmetric key throughout Rosenpass.
pub const KEY_LEN: usize = 32;
/// The hash is used as a symmetric key and should have the same length.
pub const HASH_LEN: usize = KEY_LEN;
/// Provides a way to pick which keyed hash to use at runtime.
/// Implements [`KeyedHashInstance`] to allow hashing using the respective algorithm.
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum KeyedHash {
/// A hasher backed by [`SHAKE256_32`].
KeyedShake256(SHAKE256_32),
/// A hasher backed by [`IncorrectHmacBlake2b`].
IncorrectHmacBlake2b(IncorrectHmacBlake2b),
}
impl KeyedHash {
/// Creates an [`KeyedHash`] backed by SHAKE256.
pub fn keyed_shake256() -> Self {
Self::KeyedShake256(Default::default())
}
/// Creates an [`KeyedHash`] backed by Blake2B.
pub fn incorrect_hmac_blake2b() -> Self {
Self::IncorrectHmacBlake2b(Default::default())
}
}
impl KeyedHashInstance<KEY_LEN, HASH_LEN> for KeyedHash {
type Error = anyhow::Error;
fn keyed_hash(
&self,
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error> {
match self {
Self::KeyedShake256(h) => h.keyed_hash(key, data, out)?,
Self::IncorrectHmacBlake2b(h) => h.keyed_hash(key, data, out)?,
};
Ok(())
}
}
impl Display for KeyedHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::KeyedShake256(_) => write!(f, "KeyedShake256_32"),
Self::IncorrectHmacBlake2b(_) => write!(f, "IncorrectHmacBlake2b"),
}
}
}

View File

@@ -0,0 +1,88 @@
//! Implementation of the [`KeyedHashBlake2b`] trait based on the [`libcrux_blake2`] crate.
use libcrux_blake2::Blake2bBuilder;
use rosenpass_cipher_traits::algorithms::KeyedHashBlake2b;
use rosenpass_cipher_traits::primitives::KeyedHash;
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::HASH_LEN;
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::KEY_LEN;
/// Describles which error occurred
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// An unexpected internal error occurred. Should never be returned and points to a bug in the
/// implementation.
#[error("internal error")]
InternalError,
/// Indicates that the provided data was too long.
#[error("data is too long")]
DataTooLong,
}
/// Hasher for the given `data` with the Blake2b hash function.
pub struct Blake2b;
impl KeyedHash<KEY_LEN, HASH_LEN> for Blake2b {
type Error = Error;
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error> {
let mut h = Blake2bBuilder::new_keyed_const(key)
// this may fail if the key length is invalid, but 32 is fine
.map_err(|_| Error::InternalError)?
.build_const_digest_len()
.map_err(|_|
// this can only fail if the output length is invalid, but 32 is fine.
Error::InternalError)?;
h.update(data).map_err(|_| Error::DataTooLong)?;
h.finalize(out);
Ok(())
}
}
impl KeyedHashBlake2b for Blake2b {}
#[cfg(test)]
mod equivalence_tests {
use super::*;
use rand::RngCore;
#[test]
fn fuzz_equivalence_libcrux_old_new() {
let datas: [&[u8]; 3] = [
b"".as_slice(),
b"test".as_slice(),
b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
];
let mut key = [0; KEY_LEN];
let mut rng = rand::thread_rng();
let mut hash_left = [0; 32];
let mut hash_right = [0; 32];
for data in datas {
for _ in 0..1000 {
rng.fill_bytes(&mut key);
crate::subtle::rust_crypto::blake2b::Blake2b::keyed_hash(
&key,
data,
&mut hash_left,
)
.unwrap();
crate::subtle::libcrux::blake2b::Blake2b::keyed_hash(&key, data, &mut hash_right)
.unwrap();
assert_eq!(hash_left, hash_right);
}
}
}
}

View File

@@ -0,0 +1,274 @@
//! Implementation of the [`AeadChaCha20Poly1305`] trait based on the [`libcrux_chacha20poly1305`] crate.
use rosenpass_cipher_traits::algorithms::AeadChaCha20Poly1305;
use rosenpass_cipher_traits::primitives::{Aead, AeadError};
pub use rosenpass_cipher_traits::algorithms::aead_chacha20poly1305::{KEY_LEN, NONCE_LEN, TAG_LEN};
/// An implementation of the ChaCha20Poly1305 AEAD based on libcrux
pub struct ChaCha20Poly1305;
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for ChaCha20Poly1305 {
fn encrypt(
&self,
ciphertext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
plaintext: &[u8],
) -> Result<(), AeadError> {
let (ctxt, tag) = libcrux_chacha20poly1305::encrypt(key, plaintext, ciphertext, ad, nonce)
.map_err(|_| AeadError::InternalError)?;
// return an error of the destination buffer is longer than expected
// because the caller wouldn't know where the end is
if ctxt.len() + tag.len() != ciphertext.len() {
return Err(AeadError::InternalError);
}
Ok(())
}
fn decrypt(
&self,
plaintext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
ciphertext: &[u8],
) -> Result<(), AeadError> {
let ptxt = libcrux_chacha20poly1305::decrypt(key, plaintext, ciphertext, ad, nonce)
.map_err(|_| AeadError::DecryptError)?;
// return an error of the destination buffer is longer than expected
// because the caller wouldn't know where the end is
if ptxt.len() != plaintext.len() {
return Err(AeadError::DecryptError);
}
Ok(())
}
}
impl AeadChaCha20Poly1305 for ChaCha20Poly1305 {}
/// The idea of these tests is to check that the above implemenatation behaves, by and large, the
/// same as the one from the old libcrux and the one from RustCrypto. You can consider them janky,
/// self-rolled property-based tests.
#[cfg(test)]
mod equivalence_tests {
use super::*;
use rand::RngCore;
#[test]
fn proptest_equivalence_libcrux_rustcrypto() {
use crate::subtle::rust_crypto::chacha20poly1305_ietf::ChaCha20Poly1305 as RustCryptoChaCha20Poly1305;
let ptxts: [&[u8]; 3] = [
b"".as_slice(),
b"test".as_slice(),
b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
];
let mut key = [0; KEY_LEN];
let mut rng = rand::thread_rng();
let mut ctxt_left = [0; 64 + TAG_LEN];
let mut ctxt_right = [0; 64 + TAG_LEN];
let mut ptxt_left = [0; 64];
let mut ptxt_right = [0; 64];
let nonce = [0; NONCE_LEN];
let ad = b"";
for ptxt in ptxts {
for _ in 0..1000 {
rng.fill_bytes(&mut key);
let ctxt_left = &mut ctxt_left[..ptxt.len() + TAG_LEN];
let ctxt_right = &mut ctxt_right[..ptxt.len() + TAG_LEN];
let ptxt_left = &mut ptxt_left[..ptxt.len()];
let ptxt_right = &mut ptxt_right[..ptxt.len()];
RustCryptoChaCha20Poly1305
.encrypt(ctxt_left, &key, &nonce, ad, ptxt)
.unwrap();
ChaCha20Poly1305
.encrypt(ctxt_right, &key, &nonce, ad, ptxt)
.unwrap();
assert_eq!(ctxt_left, ctxt_right);
RustCryptoChaCha20Poly1305
.decrypt(ptxt_left, &key, &nonce, ad, ctxt_left)
.unwrap();
ChaCha20Poly1305
.decrypt(ptxt_right, &key, &nonce, ad, ctxt_right)
.unwrap();
assert_eq!(ptxt_left, ptxt);
assert_eq!(ptxt_right, ptxt);
}
}
}
#[test]
#[cfg(feature = "experiment_libcrux_chachapoly_test")]
fn proptest_equivalence_libcrux_old_new() {
let ptxts: [&[u8]; 3] = [
b"".as_slice(),
b"test".as_slice(),
b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
];
let mut key = [0; KEY_LEN];
let mut rng = rand::thread_rng();
let mut ctxt_left = [0; 64 + TAG_LEN];
let mut ctxt_right = [0; 64 + TAG_LEN];
let mut ptxt_left = [0; 64];
let mut ptxt_right = [0; 64];
let nonce = [0; NONCE_LEN];
let ad = b"";
for ptxt in ptxts {
for _ in 0..1000 {
rng.fill_bytes(&mut key);
let ctxt_left = &mut ctxt_left[..ptxt.len() + TAG_LEN];
let ctxt_right = &mut ctxt_right[..ptxt.len() + TAG_LEN];
let ptxt_left = &mut ptxt_left[..ptxt.len()];
let ptxt_right = &mut ptxt_right[..ptxt.len()];
encrypt(ctxt_left, &key, &nonce, ad, ptxt).unwrap();
ChaCha20Poly1305
.encrypt(ctxt_right, &key, &nonce, ad, ptxt)
.unwrap();
assert_eq!(ctxt_left, ctxt_right);
decrypt(ptxt_left, &key, &nonce, ad, ctxt_left).unwrap();
ChaCha20Poly1305
.decrypt(ptxt_right, &key, &nonce, ad, ctxt_right)
.unwrap();
assert_eq!(ptxt_left, ptxt);
assert_eq!(ptxt_right, ptxt);
}
}
// The old libcrux functions:
// The functions below are from the old libcrux backend. I am keeping them around so we can
// check if they behave the same.
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use zeroize::Zeroize;
/// Encrypts using ChaCha20Poly1305 as implemented in [libcrux](https://github.com/cryspen/libcrux).
/// Key and nonce MUST be chosen (pseudo-)randomly. The `key` slice MUST have a length of
/// [KEY_LEN]. The `nonce` slice MUST have a length of [NONCE_LEN]. The last [TAG_LEN] bytes
/// written in `ciphertext` are the tag guaranteeing integrity. `ciphertext` MUST have a capacity of
/// `plaintext.len()` + [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf_libcrux::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
///
/// const PLAINTEXT_LEN: usize = 43;
/// let plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(PLAINTEXT_LEN, plaintext.len());
/// let key: &[u8] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut ciphertext_buffer = [0u8; PLAINTEXT_LEN + TAG_LEN];
///
/// let res: anyhow::Result<()> = encrypt(&mut ciphertext_buffer, key, nonce, additional_data, plaintext);
/// assert!(res.is_ok());
/// # let expected_ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
/// # 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
/// # 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
/// # 8, 114, 85, 4, 25];
/// # assert_eq!(expected_ciphertext, &ciphertext_buffer);
///```
///
#[inline]
pub fn encrypt(
ciphertext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
plaintext: &[u8],
) -> anyhow::Result<()> {
let (ciphertext, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
use libcrux::aead as C;
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
let crux_iv = C::Iv(nonce.try_into().unwrap());
copy_slice(plaintext).to(ciphertext);
let crux_tag = libcrux::aead::encrypt(&crux_key, ciphertext, crux_iv, ad).unwrap();
copy_slice(crux_tag.as_ref()).to(mac);
match crux_key {
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
_ => panic!(),
}
Ok(())
}
/// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data
/// `ad`. using ChaCha20Poly1305 as implemented in [libcrux](https://github.com/cryspen/libcrux).
///
/// The `key` slice MUST have a length of [KEY_LEN]. The `nonce` slice MUST have a length of
/// [NONCE_LEN]. The plaintext buffer must have a capacity of `ciphertext.len()` - [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::chacha20poly1305_ietf_libcrux::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
/// let ciphertext: &[u8] = &[239, 104, 148, 202, 120, 32, 77, 27, 246, 206, 226, 17,
/// 83, 78, 122, 116, 187, 123, 70, 199, 58, 130, 21, 1, 107, 230, 58, 77, 18, 152, 31, 159, 80,
/// 151, 72, 27, 236, 137, 60, 55, 180, 31, 71, 97, 199, 12, 60, 155, 70, 221, 225, 110, 132, 191,
/// 8, 114, 85, 4, 25]; // this is the ciphertext generated by the example for the encryption
/// const PLAINTEXT_LEN: usize = 43;
/// assert_eq!(PLAINTEXT_LEN + TAG_LEN, ciphertext.len());
///
/// let key: &[u8] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut plaintext_buffer = [0u8; PLAINTEXT_LEN];
///
/// let res: anyhow::Result<()> = decrypt(&mut plaintext_buffer, key, nonce, additional_data, ciphertext);
/// assert!(res.is_ok());
/// let expected_plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(expected_plaintext, plaintext_buffer);
///
///```
#[inline]
pub fn decrypt(
plaintext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<()> {
let (ciphertext, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
use libcrux::aead as C;
let crux_key = C::Key::Chacha20Poly1305(C::Chacha20Key(key.try_into().unwrap()));
let crux_iv = C::Iv(nonce.try_into().unwrap());
let crux_tag = C::Tag::from_slice(mac).unwrap();
copy_slice(ciphertext).to(plaintext);
libcrux::aead::decrypt(&crux_key, plaintext, crux_iv, ad, &crux_tag).unwrap();
match crux_key {
C::Key::Chacha20Poly1305(mut k) => k.0.zeroize(),
_ => panic!(),
}
Ok(())
}
}
}

View File

@@ -0,0 +1,133 @@
//! Implementation of the [`KemKyber512`] trait based on the [`libcrux_ml_kem`] crate.
use libcrux_ml_kem::kyber512;
use rand::RngCore;
use rosenpass_cipher_traits::algorithms::KemKyber512;
use rosenpass_cipher_traits::primitives::{Kem, KemError};
pub use rosenpass_cipher_traits::algorithms::kem_kyber512::{CT_LEN, PK_LEN, SHK_LEN, SK_LEN};
/// An implementation of the Kyber512 KEM based on libcrux
pub struct Kyber512;
impl Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> for Kyber512 {
fn keygen(&self, sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), KemError> {
let mut randomness = [0u8; libcrux_ml_kem::KEY_GENERATION_SEED_SIZE];
rand::thread_rng().fill_bytes(&mut randomness);
let key_pair = kyber512::generate_key_pair(randomness);
let new_sk: &[u8; SK_LEN] = key_pair.sk();
let new_pk: &[u8; PK_LEN] = key_pair.pk();
sk.clone_from_slice(new_sk);
pk.clone_from_slice(new_pk);
Ok(())
}
fn encaps(
&self,
shk: &mut [u8; SHK_LEN],
ct: &mut [u8; CT_LEN],
pk: &[u8; PK_LEN],
) -> Result<(), KemError> {
let mut randomness = [0u8; libcrux_ml_kem::SHARED_SECRET_SIZE];
rand::thread_rng().fill_bytes(&mut randomness);
let (new_ct, new_shk) = kyber512::encapsulate(&pk.into(), randomness);
let new_ct: &[u8; CT_LEN] = new_ct.as_slice();
shk.clone_from_slice(&new_shk);
ct.clone_from_slice(new_ct);
Ok(())
}
fn decaps(
&self,
shk: &mut [u8; SHK_LEN],
sk: &[u8; SK_LEN],
ct: &[u8; CT_LEN],
) -> Result<(), KemError> {
let new_shk: [u8; SHK_LEN] = kyber512::decapsulate(&sk.into(), &ct.into());
shk.clone_from(&new_shk);
Ok(())
}
}
impl Default for Kyber512 {
fn default() -> Self {
Self
}
}
impl KemKyber512 for Kyber512 {}
#[cfg(test)]
mod equivalence_tests {
use super::*;
// Test that libcrux and OQS produce the same results
#[test]
fn proptest_equivalence_libcrux_oqs() {
use rosenpass_oqs::Kyber512 as OqsKyber512;
let (mut sk1, mut pk1) = ([0; SK_LEN], [0; PK_LEN]);
let (mut sk2, mut pk2) = ([0; SK_LEN], [0; PK_LEN]);
let mut ct_left = [0; CT_LEN];
let mut ct_right = [0; CT_LEN];
let mut shk_enc_left = [0; SHK_LEN];
let mut shk_enc_right = [0; SHK_LEN];
// naming schema: shk_dec_{encapsing lib}_{decapsing lib}
// should be the same if the encapsing lib was the same.
let mut shk_dec_left_left = [0; SHK_LEN];
let mut shk_dec_left_right = [0; SHK_LEN];
let mut shk_dec_right_left = [0; SHK_LEN];
let mut shk_dec_right_right = [0; SHK_LEN];
for _ in 0..1000 {
let sk1 = &mut sk1;
let pk1 = &mut pk1;
let sk2 = &mut sk2;
let pk2 = &mut pk2;
let ct_left = &mut ct_left;
let ct_right = &mut ct_right;
let shk_enc_left = &mut shk_enc_left;
let shk_enc_right = &mut shk_enc_right;
let shk_dec_left_left = &mut shk_dec_left_left;
let shk_dec_left_right = &mut shk_dec_left_right;
let shk_dec_right_left = &mut shk_dec_right_left;
let shk_dec_right_right = &mut shk_dec_right_right;
Kyber512.keygen(sk1, pk1).unwrap();
Kyber512.keygen(sk2, pk2).unwrap();
Kyber512.encaps(shk_enc_left, ct_left, pk2).unwrap();
OqsKyber512.encaps(shk_enc_right, ct_right, pk2).unwrap();
Kyber512.decaps(shk_dec_left_left, sk2, ct_left).unwrap();
Kyber512.decaps(shk_dec_right_left, sk2, ct_right).unwrap();
OqsKyber512
.decaps(shk_dec_left_right, sk2, ct_left)
.unwrap();
OqsKyber512
.decaps(shk_dec_right_right, sk2, ct_right)
.unwrap();
assert_eq!(shk_enc_left, shk_dec_left_left);
assert_eq!(shk_enc_left, shk_dec_left_right);
assert_eq!(shk_enc_right, shk_dec_right_left);
assert_eq!(shk_enc_right, shk_dec_right_right);
}
}
}

View File

@@ -0,0 +1,14 @@
//! Implementations backed by libcrux, a verified crypto library.
//!
//! [Website](https://cryspen.com/libcrux/)
//!
//! [Github](https://github.com/cryspen/libcrux)
#[cfg(feature = "experiment_libcrux_blake2")]
pub mod blake2b;
#[cfg(feature = "experiment_libcrux_chachapoly")]
pub mod chacha20poly1305_ietf;
#[cfg(feature = "experiment_libcrux_kyber")]
pub mod kyber512;

View File

@@ -1,7 +1,16 @@
pub mod blake2b;
#[cfg(not(feature = "experiment_libcrux"))]
pub mod chacha20poly1305_ietf;
#[cfg(feature = "experiment_libcrux")]
pub mod chacha20poly1305_ietf_libcrux;
pub mod incorrect_hmac_blake2b;
pub mod xchacha20poly1305_ietf;
//! Contains the implementations of the crypto algorithms used throughout Rosenpass.
pub mod keyed_hash;
pub use custom::incorrect_hmac_blake2b;
pub use rust_crypto::{blake2b, keyed_shake256};
pub mod custom;
pub mod rust_crypto;
#[cfg(any(
feature = "experiment_libcrux_blake2",
feature = "experiment_libcrux_chachapoly",
feature = "experiment_libcrux_kyber"
))]
pub mod libcrux;

View File

@@ -0,0 +1,44 @@
use zeroize::Zeroizing;
use blake2::digest::crypto_common::generic_array::GenericArray;
use blake2::digest::crypto_common::typenum::U32;
use blake2::digest::{FixedOutput, Mac};
use blake2::Blake2bMac;
use rosenpass_cipher_traits::primitives::KeyedHash;
use rosenpass_to::{ops::copy_slice, To};
pub use rosenpass_cipher_traits::algorithms::keyed_hash_blake2b::{HASH_LEN, KEY_LEN};
/// Specify that the used implementation of BLAKE2b is the MAC version of BLAKE2b
/// with output and key length of 32 bytes (see [Blake2bMac]).
type Impl = Blake2bMac<U32>;
/// Hashes the given `data` with the [Blake2bMac] hash function under the given `key`.
/// The both the length of the output the length of the key 32 bytes (or 256 bits).
pub struct Blake2b;
impl KeyedHash<KEY_LEN, HASH_LEN> for Blake2b {
type Error = anyhow::Error;
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error> {
let mut h = Impl::new_from_slice(key)?;
h.update(data);
// Jesus christ, blake2 crate, your usage of GenericArray might be nice and fancy,
// but it introduces a ton of complexity. This cost me half an hour just to figure
// out the right way to use the imports while allowing for zeroization.
// An API based on slices might actually be simpler.
let mut tmp = Zeroizing::new([0u8; HASH_LEN]);
let tmp = GenericArray::from_mut_slice(tmp.as_mut());
h.finalize_into(tmp);
copy_slice(tmp.as_ref()).to(out);
Ok(())
}
}
impl rosenpass_cipher_traits::algorithms::KeyedHashBlake2b for Blake2b {}

View File

@@ -0,0 +1,79 @@
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use rosenpass_cipher_traits::algorithms::AeadChaCha20Poly1305;
use rosenpass_cipher_traits::primitives::{Aead, AeadError};
use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::ChaCha20Poly1305 as AeadImpl;
use chacha20poly1305::{AeadInPlace, KeyInit};
pub use rosenpass_cipher_traits::algorithms::aead_chacha20poly1305::{KEY_LEN, NONCE_LEN, TAG_LEN};
/// Implements the [`Aead`] and [`AeadChaCha20Poly1305`] traits backed by the RustCrypto
/// implementation.
pub struct ChaCha20Poly1305;
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for ChaCha20Poly1305 {
fn encrypt(
&self,
ciphertext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
plaintext: &[u8],
) -> Result<(), AeadError> {
// The comparison looks complicated, but we need to do it this way to prevent
// over/underflows.
if ciphertext.len() < TAG_LEN || ciphertext.len() - TAG_LEN < plaintext.len() {
return Err(AeadError::InvalidLengths);
}
let nonce = GenericArray::from_slice(nonce);
let (ct, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
copy_slice(plaintext).to(ct);
// This only fails if the length is wrong, which really shouldn't happen and would
// constitute an internal error.
let encrypter = AeadImpl::new_from_slice(key).map_err(|_| AeadError::InternalError)?;
let mac_value = encrypter
.encrypt_in_place_detached(nonce, ad, ct)
.map_err(|_| AeadError::InternalError)?;
copy_slice(&mac_value[..]).to(mac);
Ok(())
}
fn decrypt(
&self,
plaintext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
ciphertext: &[u8],
) -> Result<(), AeadError> {
// The comparison looks complicated, but we need to do it this way to prevent
// over/underflows.
if ciphertext.len() < TAG_LEN || ciphertext.len() - TAG_LEN < plaintext.len() {
return Err(AeadError::InvalidLengths);
}
let nonce = GenericArray::from_slice(nonce);
let (ct, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
let tag = GenericArray::from_slice(mac);
copy_slice(ct).to(plaintext);
// This only fails if the length is wrong, which really shouldn't happen and would
// constitute an internal error.
let decrypter = AeadImpl::new_from_slice(key).map_err(|_| AeadError::InternalError)?;
decrypter
.decrypt_in_place_detached(nonce, ad, plaintext, tag)
.map_err(|_| AeadError::DecryptError)?;
Ok(())
}
}
impl AeadChaCha20Poly1305 for ChaCha20Poly1305 {}

View File

@@ -0,0 +1,117 @@
use anyhow::ensure;
use rosenpass_cipher_traits::primitives::{InferKeyedHash, KeyedHash};
use sha3::digest::{ExtendableOutput, Update, XofReader};
use sha3::Shake256;
pub use rosenpass_cipher_traits::algorithms::keyed_hash_shake256::{HASH_LEN, KEY_LEN};
/// An implementation of the [`KeyedHash`] trait backed by the RustCrypto implementation of SHAKE256.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SHAKE256Core<const KEY_LEN: usize, const HASH_LEN: usize>;
impl<const KEY_LEN: usize, const HASH_LEN: usize> KeyedHash<KEY_LEN, HASH_LEN>
for SHAKE256Core<KEY_LEN, HASH_LEN>
{
type Error = anyhow::Error;
/// Provides a keyed hash function based on SHAKE256. To work for the protocol, the output length
/// and key length are fixed to 32 bytes (also see [KEY_LEN] and [HASH_LEN]).
///
/// Note that the SHAKE256 is designed for 64 bytes output length, which we truncate to 32 bytes
/// to work well with the overall protocol. Referring to Table 4 of FIPS 202, this offers the
/// same collision resistance as SHAKE128, but 256 bits of preimage resistance. We therefore
/// prefer a truncated SHAKE256 over SHAKE128.
///
/// #Examples
/// ```rust
/// # use rosenpass_ciphers::subtle::rust_crypto::keyed_shake256::SHAKE256Core;
/// use rosenpass_cipher_traits::primitives::KeyedHash;
/// const KEY_LEN: usize = 32;
/// const HASH_LEN: usize = 32;
/// let key: [u8; 32] = [0; KEY_LEN];
/// let data: [u8; 32] = [255; 32]; // arbitrary data, could also be longer
/// // buffer for the hash output
/// let mut hash_data: [u8; 32] = [0u8; HASH_LEN];
///
/// assert!(SHAKE256Core::<32, 32>::keyed_hash(&key, &data, &mut hash_data).is_ok(), "Hashing has to return OK result");
/// # let expected_hash: &[u8] = &[174, 4, 47, 188, 1, 228, 179, 246, 67, 43, 255, 94, 155, 11,
/// 187, 161, 38, 110, 217, 23, 4, 62, 172, 30, 218, 187, 249, 80, 171, 21, 145, 238];
/// # assert_eq!(hash_data, expected_hash);
/// ```
fn keyed_hash(
key: &[u8; KEY_LEN],
data: &[u8],
out: &mut [u8; HASH_LEN],
) -> Result<(), Self::Error> {
// Since SHAKE256 is a XOF, we fix the output length manually to what is required for the
// protocol.
ensure!(out.len() == HASH_LEN);
// Not bothering with padding; the implementation
// uses appropriately sized keys.
ensure!(key.len() == KEY_LEN);
let mut shake256 = Shake256::default();
shake256.update(key);
shake256.update(data);
// Since we use domain separation extensively, related outputs of the truncated XOF
// are not a concern. This follows the NIST recommendations in Section A.2 of the FIPS 202
// standard, (pages 24/25, i.e., 32/33 in the PDF).
shake256.finalize_xof().read(out);
Ok(())
}
}
impl<const KEY_LEN: usize, const HASH_LEN: usize> SHAKE256Core<KEY_LEN, HASH_LEN> {
pub fn new() -> Self {
Self
}
}
impl<const KEY_LEN: usize, const HASH_LEN: usize> Default for SHAKE256Core<KEY_LEN, HASH_LEN> {
fn default() -> Self {
Self::new()
}
}
/// This type provides the same functionality as [SHAKE256Core], but bound to an instance.
/// In contrast to [SHAKE256Core], this allows for type interference and thus allows the user of the
/// type to omit explicit type parameters when instantiating the type or using it.
///
/// The instantiation is based on the [InferKeyedHash] trait.
///
/// ```rust
/// # use rosenpass_ciphers::subtle::rust_crypto::keyed_shake256::{SHAKE256};
/// use rosenpass_cipher_traits::primitives::KeyedHashInstance;
/// const KEY_LEN: usize = 32;
/// const HASH_LEN: usize = 32;
/// let key: [u8; KEY_LEN] = [0; KEY_LEN];
/// let data: [u8; 32] = [255; 32]; // arbitrary data, could also be longer
/// // buffer for the hash output
/// let mut hash_data: [u8; 32] = [0u8; HASH_LEN];
/// assert!(SHAKE256::new().keyed_hash(&key, &data, &mut hash_data).is_ok(), "Hashing has to return OK result");
/// # let expected_hash: &[u8] = &[174, 4, 47, 188, 1, 228, 179, 246, 67, 43, 255, 94, 155, 11, 187,
/// 161, 38, 110, 217, 23, 4, 62, 172, 30, 218, 187, 249, 80, 171, 21, 145, 238];
/// # assert_eq!(hash_data, expected_hash);
/// ```
pub type SHAKE256<const KEY_LEN: usize, const HASH_LEN: usize> =
InferKeyedHash<SHAKE256Core<KEY_LEN, HASH_LEN>, KEY_LEN, HASH_LEN>;
/// The SHAKE256_32 type is a specific instance of the [SHAKE256] type with the key length and hash
/// length fixed to 32 bytes.
///
/// ```rust
/// # use rosenpass_ciphers::subtle::keyed_shake256::{SHAKE256_32};
/// use rosenpass_cipher_traits::primitives::KeyedHashInstance;
/// const KEY_LEN: usize = 32;
/// const HASH_LEN: usize = 32;
/// let key: [u8; 32] = [0; KEY_LEN];
/// let data: [u8; 32] = [255; 32]; // arbitrary data, could also be longer
/// // buffer for the hash output
/// let mut hash_data: [u8; 32] = [0u8; HASH_LEN];
///
/// assert!(SHAKE256_32::new().keyed_hash(&key, &data, &mut hash_data).is_ok(), "Hashing has to return OK result");
/// # let expected_hash: &[u8] = &[174, 4, 47, 188, 1, 228, 179, 246, 67, 43, 255, 94, 155, 11, 187,
/// 161, 38, 110, 217, 23, 4, 62, 172, 30, 218, 187, 249, 80, 171, 21, 145, 238];
/// # assert_eq!(hash_data, expected_hash);
/// ```
pub type SHAKE256_32 = SHAKE256<32, 32>;

View File

@@ -0,0 +1,7 @@
//! Implementations backed by RustCrypto
pub mod blake2b;
pub mod keyed_shake256;
pub mod chacha20poly1305_ietf;
pub mod xchacha20poly1305_ietf;

View File

@@ -0,0 +1,164 @@
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use rosenpass_cipher_traits::algorithms::aead_xchacha20poly1305::AeadXChaCha20Poly1305;
use rosenpass_cipher_traits::primitives::{Aead, AeadError, AeadWithNonceInCiphertext};
use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::XChaCha20Poly1305 as AeadImpl;
use chacha20poly1305::{AeadInPlace, KeyInit};
pub use rosenpass_cipher_traits::algorithms::aead_xchacha20poly1305::{
KEY_LEN, NONCE_LEN, TAG_LEN,
};
/// Implements the [`Aead`] and [`AeadXChaCha20Poly1305`] traits backed by the RustCrypto
/// implementation.
pub struct XChaCha20Poly1305;
impl Aead<KEY_LEN, NONCE_LEN, TAG_LEN> for XChaCha20Poly1305 {
fn encrypt(
&self,
ciphertext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
plaintext: &[u8],
) -> Result<(), AeadError> {
// The comparison looks complicated, but we need to do it this way to prevent
// over/underflows.
if ciphertext.len() < TAG_LEN || ciphertext.len() - TAG_LEN < plaintext.len() {
return Err(AeadError::InvalidLengths);
}
let (ct, mac) = ciphertext.split_at_mut(ciphertext.len() - TAG_LEN);
copy_slice(plaintext).to(ct);
let nonce = GenericArray::from_slice(nonce);
// This only fails if the length is wrong, which really shouldn't happen and would
// constitute an internal error.
let encrypter = AeadImpl::new_from_slice(key).map_err(|_| AeadError::InternalError)?;
let mac_value = encrypter
.encrypt_in_place_detached(nonce, ad, ct)
.map_err(|_| AeadError::InternalError)?;
copy_slice(&mac_value[..]).to(mac);
Ok(())
}
fn decrypt(
&self,
plaintext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
ciphertext: &[u8],
) -> Result<(), AeadError> {
// The comparison looks complicated, but we need to do it this way to prevent
// over/underflows.
if ciphertext.len() < TAG_LEN || ciphertext.len() - TAG_LEN < plaintext.len() {
return Err(AeadError::InvalidLengths);
}
let (ct, mac) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
let nonce = GenericArray::from_slice(nonce);
let tag = GenericArray::from_slice(mac);
copy_slice(ct).to(plaintext);
// This only fails if the length is wrong, which really shouldn't happen and would
// constitute an internal error.
let decrypter = AeadImpl::new_from_slice(key).map_err(|_| AeadError::InternalError)?;
decrypter
.decrypt_in_place_detached(nonce, ad, plaintext, tag)
.map_err(|_| AeadError::DecryptError)?;
Ok(())
}
}
impl AeadXChaCha20Poly1305 for XChaCha20Poly1305 {}
/// Encrypts using XChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305).
/// `key` and `nonce` MUST be chosen (pseudo-)randomly. The `key` slice MUST have a length of
/// [KEY_LEN]. The `nonce` slice MUST have a length of [NONCE_LEN].
/// In contrast to [chacha20poly1305_ietf::encrypt](crate::subtle::chacha20poly1305_ietf::encrypt) and
/// [chacha20poly1305_ietf_libcrux::encrypt](crate::subtle::chacha20poly1305_ietf_libcrux::encrypt),
/// `nonce` is also written into `ciphertext` and therefore ciphertext MUST have a length
/// of at least [NONCE_LEN] + `plaintext.len()` + [TAG_LEN].
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::rust_crypto::xchacha20poly1305_ietf::{encrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
/// const PLAINTEXT_LEN: usize = 43;
/// let plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(PLAINTEXT_LEN, plaintext.len());
/// let key: &[u8; KEY_LEN] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8; NONCE_LEN] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut ciphertext_buffer = [0u8; NONCE_LEN + PLAINTEXT_LEN + TAG_LEN];
///
///
/// let res: anyhow::Result<()> = encrypt(&mut ciphertext_buffer, key, nonce, additional_data, plaintext);
/// # assert!(res.is_ok());
/// # let expected_ciphertext: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/// # 0, 0, 0, 0, 8, 241, 229, 253, 200, 81, 248, 30, 183, 149, 134, 168, 149, 87, 109, 49, 159, 108,
/// # 206, 89, 51, 232, 232, 197, 163, 253, 254, 208, 73, 76, 253, 13, 247, 162, 133, 184, 177, 44,
/// # 73, 138, 176, 193, 61, 248, 61, 183, 164, 192, 214, 168, 4, 1, 62, 243, 36, 48, 149, 164, 6];
/// # assert_eq!(expected_ciphertext, &ciphertext_buffer);
///```
#[inline]
pub fn encrypt(
ciphertext: &mut [u8],
key: &[u8; KEY_LEN],
nonce: &[u8; NONCE_LEN],
ad: &[u8],
plaintext: &[u8],
) -> anyhow::Result<()> {
XChaCha20Poly1305
.encrypt_with_nonce_in_ctxt(ciphertext, key, nonce, ad, plaintext)
.map_err(anyhow::Error::from)
}
/// Decrypts a `ciphertext` and verifies the integrity of the `ciphertext` and the additional data
/// `ad`. using XChaCha20Poly1305 as implemented in [RustCrypto](https://github.com/RustCrypto/AEADs/tree/master/chacha20poly1305).
///
/// The `key` slice MUST have a length of [KEY_LEN]. The `nonce` slice MUST have a length of
/// [NONCE_LEN]. The plaintext buffer must have a capacity of `ciphertext.len()` - [TAG_LEN] - [NONCE_LEN].
///
/// In contrast to [chacha20poly1305_ietf::decrypt](crate::subtle::chacha20poly1305_ietf::decrypt) and
/// [chacha20poly1305_ietf_libcrux::decrypt](crate::subtle::chacha20poly1305_ietf_libcrux::decrypt),
/// `ciperhtext` MUST include the as it is not given otherwise.
///
/// # Examples
///```rust
/// # use rosenpass_ciphers::subtle::rust_crypto::xchacha20poly1305_ietf::{decrypt, TAG_LEN, KEY_LEN, NONCE_LEN};
/// let ciphertext: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/// # 0, 0, 0, 0, 8, 241, 229, 253, 200, 81, 248, 30, 183, 149, 134, 168, 149, 87, 109, 49, 159, 108,
/// # 206, 89, 51, 232, 232, 197, 163, 253, 254, 208, 73, 76, 253, 13, 247, 162, 133, 184, 177, 44,
/// # 73, 138, 176, 193, 61, 248, 61, 183, 164, 192, 214, 168, 4, 1, 62, 243, 36, 48, 149, 164, 6];
/// // this is the ciphertext generated by the example for the encryption
/// const PLAINTEXT_LEN: usize = 43;
/// assert_eq!(PLAINTEXT_LEN + TAG_LEN + NONCE_LEN, ciphertext.len());
///
/// let key: &[u8; KEY_LEN] = &[0u8; KEY_LEN]; // THIS IS NOT A SECURE KEY
/// let nonce: &[u8; NONCE_LEN] = &[0u8; NONCE_LEN]; // THIS IS NOT A SECURE NONCE
/// let additional_data: &[u8] = "the encrypted message is very important".as_bytes();
/// let mut plaintext_buffer = [0u8; PLAINTEXT_LEN];
///
/// let res: anyhow::Result<()> = decrypt(&mut plaintext_buffer, key, additional_data, ciphertext);
/// assert!(res.is_ok());
/// let expected_plaintext = "post-quantum cryptography is very important".as_bytes();
/// assert_eq!(expected_plaintext, plaintext_buffer);
///
///```
#[inline]
pub fn decrypt(
plaintext: &mut [u8],
key: &[u8; KEY_LEN],
ad: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<()> {
XChaCha20Poly1305
.decrypt_with_nonce_in_ctxt(plaintext, key, ad, ciphertext)
.map_err(anyhow::Error::from)
}

View File

@@ -1,45 +0,0 @@
use rosenpass_to::ops::copy_slice;
use rosenpass_to::To;
use rosenpass_util::typenum2const;
use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::XChaCha20Poly1305 as AeadImpl;
use chacha20poly1305::{AeadCore, AeadInPlace, KeyInit, KeySizeUser};
pub const KEY_LEN: usize = typenum2const! { <AeadImpl as KeySizeUser>::KeySize };
pub const TAG_LEN: usize = typenum2const! { <AeadImpl as AeadCore>::TagSize };
pub const NONCE_LEN: usize = typenum2const! { <AeadImpl as AeadCore>::NonceSize };
#[inline]
pub fn encrypt(
ciphertext: &mut [u8],
key: &[u8],
nonce: &[u8],
ad: &[u8],
plaintext: &[u8],
) -> anyhow::Result<()> {
let nonce = GenericArray::from_slice(nonce);
let (n, ct_mac) = ciphertext.split_at_mut(NONCE_LEN);
let (ct, mac) = ct_mac.split_at_mut(ct_mac.len() - TAG_LEN);
copy_slice(nonce).to(n);
copy_slice(plaintext).to(ct);
let mac_value = AeadImpl::new_from_slice(key)?.encrypt_in_place_detached(nonce, ad, ct)?;
copy_slice(&mac_value[..]).to(mac);
Ok(())
}
#[inline]
pub fn decrypt(
plaintext: &mut [u8],
key: &[u8],
ad: &[u8],
ciphertext: &[u8],
) -> anyhow::Result<()> {
let (n, ct_mac) = ciphertext.split_at(NONCE_LEN);
let (ct, mac) = ct_mac.split_at(ct_mac.len() - TAG_LEN);
let nonce = GenericArray::from_slice(n);
let tag = GenericArray::from_slice(mac);
copy_slice(ct).to(plaintext);
AeadImpl::new_from_slice(key)?.decrypt_in_place_detached(nonce, ad, plaintext, tag)?;
Ok(())
}

View File

@@ -8,6 +8,7 @@ description = "Rosenpass internal utilities for constant time crypto implementat
homepage = "https://rosenpass.eu/"
repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
rust-version = "1.77"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -19,4 +20,7 @@ rosenpass-to = { workspace = true }
memsec = { workspace = true }
[dev-dependencies]
rand = "0.8.5"
rand = { workspace = true }
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }

View File

@@ -2,14 +2,29 @@
use core::ptr;
/// Little endian memcmp version of quinier/memsec
/// https://github.com/quininer/memsec/blob/bbc647967ff6d20d6dccf1c85f5d9037fcadd3b0/src/lib.rs#L30
/// Little endian memcmp version of [quinier/memsec](https://github.com/quininer/memsec/blob/bbc647967ff6d20d6dccf1c85f5d9037fcadd3b0/src/lib.rs#L30)
///
/// # Panic & Safety
///
/// Both input arrays must be at least of the indicated length.
///
/// See [std::ptr::read_volatile] on safety.
///
/// # Examples
/// ```
/// let a = [1, 2, 3, 4];
/// let b = [1, 2, 3, 4];
/// let c = [1, 2, 2, 5];
/// let d = [1, 2, 2, 4];
///
/// unsafe {
/// use rosenpass_constant_time::memcmp_le;
/// assert_eq!(memcmp_le(a.as_ptr(), b.as_ptr(), 4), 0);
/// assert!(memcmp_le(a.as_ptr(), c.as_ptr(), 4) < 0);
/// assert!(memcmp_le(a.as_ptr(), d.as_ptr(), 4) > 0);
/// assert_eq!(memcmp_le(a.as_ptr(), b.as_ptr(), 2), 0);
/// }
/// ```
#[inline(never)]
pub unsafe fn memcmp_le(b1: *const u8, b2: *const u8, len: usize) -> i32 {
let mut res = 0;
@@ -77,3 +92,23 @@ pub fn compare(a: &[u8], b: &[u8]) -> i32 {
assert!(a.len() == b.len());
unsafe { memcmp_le(a.as_ptr(), b.as_ptr(), a.len()) }
}
#[cfg(test)]
mod tests {
use crate::compare::memcmp_le;
#[test]
fn memcmp_le_test() {
let a = [1, 2, 3, 4];
let b = [1, 2, 3, 4];
let c = [1, 2, 2, 5];
let d = [1, 2, 2, 4];
unsafe {
assert_eq!(memcmp_le(a.as_ptr(), b.as_ptr(), 4), 0);
assert!(memcmp_le(a.as_ptr(), c.as_ptr(), 4) < 0);
assert!(memcmp_le(a.as_ptr(), d.as_ptr(), 4) > 0);
assert_eq!(memcmp_le(a.as_ptr(), b.as_ptr(), 2), 0);
}
}
}

View File

@@ -6,8 +6,16 @@ use core::hint::black_box;
/// and increment that integer.
///
/// # Leaks
/// TODO: mention here if this function leaks any information, see
/// <https://github.com/rosenpass/rosenpass/issues/232>
/// This function may leak timing information in the following ways:
///
/// - The function execution time is linearly proportional to the input length
/// - The number of carry operations that occur may affect timing slightly
/// - Memory access patterns are sequential and predictable
///
/// The carry operation timing variation is mitigated through the use of black_box,
/// but the linear scaling with input size is inherent to the operation.
/// These timing characteristics are generally considered acceptable for most
/// cryptographic counter implementations.
///
/// ## Tests
/// For discussion on how to ensure the constant-time execution of this function, see

View File

@@ -7,6 +7,32 @@
//! ## TODO
//! Figure out methodology to ensure that code is actually constant time, see
//! <https://github.com/rosenpass/rosenpass/issues/232>
//!
//! # Examples
//!
//! ```rust
//! use rosenpass_constant_time::{memcmp, compare};
//!
//! let a = [1, 2, 3, 4];
//! let b = [1, 2, 3, 4];
//! let c = [1, 2, 3, 5];
//!
//! // Compare for equality
//! assert!(memcmp(&a, &b));
//! assert!(!memcmp(&a, &c));
//!
//! // Compare lexicographically
//! assert_eq!(compare(&a, &c), -1); // a < c
//! assert_eq!(compare(&c, &a), 1); // c > a
//! assert_eq!(compare(&a, &b), 0); // a == b
//! ```
//!
//! # Security Notes
//!
//! While these functions aim to be constant-time, they may leak timing information in some cases:
//!
//! - Length mismatches between inputs are immediately detectable
//! - Execution time scales linearly with input size
mod compare;
mod increment;
@@ -14,6 +40,7 @@ mod memcmp;
mod xor;
pub use compare::compare;
pub use compare::memcmp_le;
pub use increment::increment;
pub use memcmp::memcmp;
pub use xor::xor;

View File

@@ -32,8 +32,11 @@ pub fn memcmp(a: &[u8], b: &[u8]) -> bool {
/// For discussion on how to (further) ensure the constant-time execution of this function,
/// see <https://github.com/rosenpass/rosenpass/issues/232>
#[cfg(all(test, feature = "constant_time_tests"))]
// Stopgap measure against https://github.com/rosenpass/rosenpass/issues/634
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
mod tests {
use super::*;
use core::hint::black_box;
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::time::Instant;
@@ -50,14 +53,12 @@ mod tests {
fn memcmp_runs_in_constant_time() {
// prepare data to compare
let n: usize = 1E6 as usize; // number of comparisons to run
let len = 1024; // length of each slice passed as parameters to the tested comparison function
let a1 = "a".repeat(len);
let a2 = a1.clone();
let b = "b".repeat(len);
const LEN: usize = 1024; // length of each slice passed as parameters to the tested comparison function
let a1 = a1.as_bytes();
let a2 = a2.as_bytes();
let b = b.as_bytes();
let a = [b'a'; LEN];
let b = [b'b'; LEN];
let mut tmp = [0u8; LEN];
// vector representing all timing tests
//
@@ -71,12 +72,14 @@ mod tests {
// run comparisons / call function to test
for test in tests.iter_mut() {
let src = match test.0 {
true => a,
false => b,
};
tmp.copy_from_slice(&src);
let now = Instant::now();
if test.0 {
memcmp(a1, a2);
} else {
memcmp(a1, b);
}
memcmp(black_box(&a), black_box(&tmp));
test.1 = now.elapsed();
// println!("eq: {}, elapsed: {:.2?}", test.0, test.1);
}
@@ -113,6 +116,7 @@ mod tests {
// Pearson correlation
let correlation = cv / (sd_x * sd_y);
println!("correlation: {:.6?}", correlation);
#[cfg(not(coverage))]
assert!(
correlation.abs() < 0.01,
"execution time correlates with result"

View File

@@ -5,12 +5,23 @@ use rosenpass_to::{with_destination, To};
/// Xors the source into the destination
///
/// Performs a constant-time XOR operation between two byte slices
///
/// Takes a source slice and XORs it with the destination slice in-place using the
/// rosenpass_to trait for destination management.
///
/// # Panics
/// If source and destination are of different sizes.
///
/// # Leaks
/// TODO: mention here if this function leaks any information, see
/// <https://github.com/rosenpass/rosenpass/issues/232>
/// This function may leak timing information in the following ways:
///
/// - The function execution time is linearly proportional to the input length
/// - Length mismatches between source and destination are immediately detectable via panic
/// - Memory access patterns follow a predictable sequential pattern
///
/// These leaks are generally considered acceptable in most cryptographic contexts
/// as they don't reveal information about the actual content being XORed.
///
/// ## Tests
/// For discussion on how to ensure the constant-time execution of this function, see

46
coverage_report.sh Executable file
View File

@@ -0,0 +1,46 @@
#! /usr/bin/env bash
set -e -o pipefail
OUTPUT_DIR="target/grcov"
log() {
echo >&2 "$@"
}
exc() {
echo '$' "$@"
"$@"
}
main() {
exc cd "$(dirname "$0")"
local open="0"
if [[ "$1" == "--open" ]]; then
open="1"
fi
exc cargo llvm-cov --all-features --workspace --doctests --branch
exc rm -rf target/llvm-cov-target/debug/deps/doctestbins
exc mv -v target/llvm-cov-target/doctestbins target/llvm-cov-target/debug/deps/
exc rm -rf "${OUTPUT_DIR}"
exc mkdir -p "${OUTPUT_DIR}"
exc grcov target/llvm-cov-target/ --llvm -s . --branch \
--binary-path ./target/llvm-cov-target/debug/deps \
--ignore-not-existing --ignore '../*' --ignore "/*" \
--excl-line '^\s*#\[(derive|repr)\(' \
-t lcov,html,markdown -o "${OUTPUT_DIR}"
if (( "${open}" == 1 )); then
xdg-open "${PWD}/${OUTPUT_DIR}/html/index.html"
fi
log ""
log "Generated reports in \"${PWD}/${OUTPUT_DIR}\"."
log "Open \"${PWD}/${OUTPUT_DIR}/html/index.html\" to view HTML report."
log ""
}
main "$@"

129
deny.toml Normal file
View File

@@ -0,0 +1,129 @@
# The graph table configures how the dependency graph is constructed and thus
# which crates the checks are performed against
[graph]
# If true, metadata will be collected with `--all-features`. Note that this can't
# be toggled off if true, if you want to conditionally enable `--all-features` it
# is recommended to pass `--all-features` on the cmd line instead
all-features = true
# If true, metadata will be collected with `--no-default-features`. The same
# caveat with `all-features` applies
no-default-features = false
# The output table provides options for how/if diagnostics are outputted
[output]
# When outputting inclusion graphs in diagnostics that include features, this
# option can be used to specify the depth at which feature edges will be added.
# This option is included since the graphs can be quite large and the addition
# of features from the crate(s) to all of the graph roots can be far too verbose.
# This option can be overridden via `--feature-depth` on the cmd line
feature-depth = 1
# This section is considered when running `cargo deny check advisories`
# More documentation for the advisories section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
ignore = [
"RUSTSEC-2024-0370",
"RUSTSEC-2024-0436",
"RUSTSEC-2023-0089",
]
# If this is true, then cargo deny will use the git executable to fetch advisory database.
# If this is false, then it uses a built-in git library.
# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
# See Git Authentication for more information about setting up git authentication.
#git-fetch-with-cli = true
# This section is considered when running `cargo deny check #licenses`
# More documentation for the licenses section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
[licenses]
# List of explicitly allowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
"MIT",
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
"BSD-3-Clause",
"ISC",
]
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
# canonical license text of a valid SPDX license file.
# [possible values: any between 0.0 and 1.0].
confidence-threshold = 0.8
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
# aren't accepted for every possible crate as with the normal allow list
exceptions = [
# Each entry is the crate and version constraint, and its specific allow
# list
{ allow = ["Unicode-DFS-2016", "Unicode-3.0"], crate = "unicode-ident" },
{ allow = ["NCSA"], crate = "libfuzzer-sys" },
]
[licenses.private]
# If true, ignores workspace crates that aren't published, or are only
# published to private registries.
# To see how to mark a crate as unpublished (to the official registry),
# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
ignore = true
# This section is considered when running `cargo deny check bans`.
# More documentation about the 'bans' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
[bans]
# Lint level for when multiple versions of the same crate are detected
multiple-versions = "warn"
# Lint level for when a crate version requirement is `*`
wildcards = "allow"
# The graph highlighting used when creating dotgraphs for crates
# with multiple versions
# * lowest-version - The path to the lowest versioned duplicate is highlighted
# * simplest-path - The path to the version with the fewest edges is highlighted
# * all - Both lowest-version and simplest-path are used
highlight = "all"
# The default lint level for `default` features for crates that are members of
# the workspace that is being checked. This can be overridden by allowing/denying
# `default` on a crate-by-crate basis if desired.
workspace-default-features = "allow"
# The default lint level for `default` features for external crates that are not
# members of the workspace. This can be overridden by allowing/denying `default`
# on a crate-by-crate basis if desired.
external-default-features = "allow"
# List of crates that are allowed. Use with care!
allow = [
]
# List of crates to deny
deny = [
]
skip-tree = [
]
# This section is considered when running `cargo deny check sources`.
# More documentation about the 'sources' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
[sources]
# Lint level for what to happen when a crate from a crate registry that is not
# in the allow list is encountered
unknown-registry = "warn"
# Lint level for what to happen when a crate from a git repository that is not
# in the allow list is encountered
unknown-git = "warn"
# List of URLs for allowed crate registries. Defaults to the crates.io index
# if not specified. If it is specified but empty, no registries are allowed.
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
# List of URLs for allowed Git repositories
allow-git = ["git+https://github.com/rosenpass/memsec.git?branch=master"]
[sources.allow-org]
# github.com organizations to allow git sources for
github = []
# gitlab.com organizations to allow git sources for
gitlab = []
# bitbucket.org organizations to allow git sources for
bitbucket = []

45
docker/Dockerfile Normal file
View File

@@ -0,0 +1,45 @@
# syntax=docker/dockerfile:1
ARG BASE_IMAGE=debian:bookworm-slim
# Stage 1: Base image with cargo-chef installed
FROM rust:latest AS chef
RUN cargo install cargo-chef
# install software required for liboqs-rust
RUN apt-get update && apt-get install -y clang cmake && rm -rf /var/lib/apt/lists/*
# Stage 2: Prepare the cargo-chef recipe
FROM chef AS planner
WORKDIR /app
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
# Stage 3: Cache dependencies using the recipe
FROM chef AS cacher
WORKDIR /app
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
# Stage 4: Build the application
FROM cacher AS builder
WORKDIR /app
COPY . .
RUN cargo build --release
# Stage 5: Install runtime-dependencies in the base image
FROM ${BASE_IMAGE} AS base_image_with_dependencies
RUN apt-get update && apt-get install -y iproute2 && rm -rf /var/lib/apt/lists/*
# Final Stage (rosenpass): Copy the rosenpass binary
FROM base_image_with_dependencies AS rosenpass
COPY --from=builder /app/target/release/rosenpass /usr/local/bin/rosenpass
ENTRYPOINT [ "/usr/local/bin/rosenpass" ]
# Final Stage (rp): Copy the rp binary
FROM base_image_with_dependencies AS rp
RUN apt-get update && apt-get install -y wireguard && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/rp /usr/local/bin/rp
ENTRYPOINT [ "/usr/local/bin/rp" ]

203
docker/USAGE.md Normal file
View File

@@ -0,0 +1,203 @@
# Rosenpass in Docker
Rosenpass provides post-quantum-secure key exchange for VPNs. It generates symmetric keys used by [WireGuard](https://www.wireguard.com/papers/wireguard.pdf) or other applications. The protocol enhances "Post-Quantum WireGuard" ([PQWG](https://eprint.iacr.org/2020/379)) with a cookie mechanism for better security against state disruption attacks.
Prebuilt Docker images are available for easy deployment:
- [`ghcr.io/rosenpass/rosenpass`](https://github.com/rosenpass/rosenpass/pkgs/container/rosenpass) the core key exchange tool
- [`ghcr.io/rosenpass/rp`](https://github.com/rosenpass/rosenpass/pkgs/container/rp) a frontend for setting up WireGuard VPNs
The entrypoint of the `rosenpass` image is the `rosenpass` executable, whose documentation can be found [here](https://rosenpass.eu/docs/rosenpass-tool/manuals/rp_manual/).
Similarly, the entrypoint of the `rp` image is the `rp` executable, with its documentation available [here](https://rosenpass.eu/docs/rosenpass-tool/manuals/rp1/).
## Usage - Standalone Key Exchange
The `ghcr.io/rosenpass/rosenpass` image can be used in a server-client setup to exchange quantum-secure shared keys.
This setup uses rosenpass as a standalone application, without using any other component such as wireguard.
What follows, is a simple setup for illustrative purposes.
Create a docker network that is used to connect the containers:
```bash
docker network create -d bridge rp
export NET=rp
```
Generate the server and client key pairs:
```bash
mkdir ./workdir-client ./workdir-server
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rosenpass \
gen-keys --public-key=workdir/server-public --secret-key=workdir/server-secret
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rosenpass \
gen-keys --public-key=workdir/client-public --secret-key=workdir/client-secret
# share the public keys between client and server
cp workdir-client/client-public workdir-server/client-public
cp workdir-server/server-public workdir-client/server-public
```
Start the server container:
```bash
docker run --name "rpserver" --network ${NET} \
-it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rosenpass \
exchange \
private-key workdir/server-secret \
public-key workdir/server-public \
listen 0.0.0.0:9999 \
peer public-key workdir/client-public \
outfile workdir/server-sharedkey
```
Find out the ip address of the server container:
```bash
EP="rpserver"
EP=$(docker inspect --format '{{ .NetworkSettings.Networks.rp.IPAddress }}' $EP)
```
Run the client container and perform the key exchange:
```bash
docker run --name "rpclient" --network ${NET} \
-it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rosenpass \
exchange \
private-key workdir/client-secret \
public-key workdir/client-public \
peer public-key workdir/server-public endpoint ${EP}:9999 \
outfile workdir/client-sharedkey
```
Now the containers will exchange shared keys and each put them into their respective outfile.
Comparing the outfiles shows that these shared keys equal:
```bash
cmp workdir-server/server-sharedkey workdir-client/client-sharedkey
```
It is now possible to set add these keys as pre-shared keys within a wireguard interface.
For example as the server,
```bash
PREKEY=$(cat workdir-server/server-sharedkey)
wg set <server-interface> peer <client-peer-public-key> preshared-key <(echo "$PREKEY")
```
## Usage - Combined with wireguard
The `ghcr.io/rosenpass/rp` image can be used to build a VPN with WireGuard and Rosenpass.
In this example, we run two containers on the same system and connect them with a bridge network within the docker overlay network.
Create the named docker network, to be able to connect the containers.
Create a docker network that is used to connect the containers:
```bash
docker network create -d bridge rp
export NET=rp
```
Generate the server and client secret keys and extract public keys.
```bash
mkdir -p ./workdir-server ./workdir-client
# server
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rp \
genkey workdir/server.rosenpass-secret
docker run -it --rm -v ./workdir-server:/workdir ghcr.io/rosenpass/rp \
pubkey workdir/server.rosenpass-secret workdir/server.rosenpass-public
# client
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rp \
genkey workdir/client.rosenpass-secret
docker run -it --rm -v ./workdir-client:/workdir ghcr.io/rosenpass/rp \
pubkey workdir/client.rosenpass-secret workdir/client.rosenpass-public
# share the public keys between client and server
cp -r workdir-client/client.rosenpass-public workdir-server/client.rosenpass-public
cp -r workdir-server/server.rosenpass-public workdir-client/server.rosenpass-public
```
Start the server container.
Note that the `NET_ADMIN` capability is neccessary, the rp command will create and manage wireguard interfaces.
Also make sure the `wireguard` kernel module is loaded by the host. (`lsmod | grep wireguard`)
```bash
docker run --name "rpserver" --network ${NET} -it -d --rm -v ./workdir-server:/workdir \
--cap-add=NET_ADMIN \
ghcr.io/rosenpass/rp \
exchange workdir/server.rosenpass-secret dev rosenpass0 \
listen 0.0.0.0:9999 peer workdir/client.rosenpass-public allowed-ips 10.0.0.0/8
```
Now find out the ip-address of the server container and then start the client container:
```bash
EP="rpserver"
EP=$(docker inspect --format '{{ .NetworkSettings.Networks.rp.IPAddress }}' $EP)
docker run --name "rpclient" --network ${NET} -it -d --rm -v ./workdir-client:/workdir \
--cap-add=NET_ADMIN \
ghcr.io/rosenpass/rp \
exchange workdir/client.rosenpass-secret dev rosenpass1 \
peer workdir/server.rosenpass-public endpoint ${EP}:9999 allowed-ips 10.0.0.1
```
Inside the docker containers assign the IP addresses:
```bash
# server
docker exec -it rpserver ip a add 10.0.0.1/24 dev rosenpass0
# client
docker exec -it rpclient ip a add 10.0.0.2/24 dev rosenpass1
```
Done! The two containers should now be connected through a wireguard VPN (Port 1000) with pre-shared keys exchanged by rosenpass (Port 9999).
Now, test the connection by starting a shell inside the client container, and ping the server through the VPN:
```bash
# client
docker exec -it rpclient bash
apt update; apt install iputils-ping
ping 10.0.0.1
```
The ping command should continuously show ping-logs:
```
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.119 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.132 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.394 ms
...
```
While the ping is running, you may stop the server container, and verify that the ping-log halts. In another terminal do:
```
docker stop -t 1 rpserver
```
## Building the Docker Images Locally
Clone the Rosenpass repository:
```
git clone https://github.com/rosenpass/rosenpass
cd rosenpass
```
Build the rp image from the root of the repository as follows:
```
docker build -f docker/Dockerfile -t ghcr.io/rosenpass/rp --target rp .
```
Build the rosenpass image from the root of the repostiry with the following command:
```
docker build -f docker/Dockerfile -t ghcr.io/rosenpass/rosenpass --target rosenpass .
```

21
flake.lock generated
View File

@@ -39,6 +39,26 @@
"type": "github"
}
},
"nix-vm-test": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1734355073,
"narHash": "sha256-FfdPOGy1zElTwKzjgIMp5K2D3gfPn6VWjVa4MJ9L1Tc=",
"owner": "numtide",
"repo": "nix-vm-test",
"rev": "5948de39a616f2261dbbf4b6f25cbe1cbefd788c",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-vm-test",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1728193676,
@@ -59,6 +79,7 @@
"inputs": {
"fenix": "fenix",
"flake-utils": "flake-utils",
"nix-vm-test": "nix-vm-test",
"nixpkgs": "nixpkgs"
}
},

View File

@@ -6,9 +6,13 @@
# for rust nightly with llvm-tools-preview
fenix.url = "github:nix-community/fenix";
fenix.inputs.nixpkgs.follows = "nixpkgs";
nix-vm-test.url = "github:numtide/nix-vm-test";
nix-vm-test.inputs.nixpkgs.follows = "nixpkgs";
nix-vm-test.inputs.flake-utils.follows = "flake-utils";
};
outputs = { self, nixpkgs, flake-utils, ... }@inputs:
outputs = { self, nixpkgs, flake-utils, nix-vm-test, ... }@inputs:
nixpkgs.lib.foldl (a: b: nixpkgs.lib.recursiveUpdate a b) { } [
@@ -77,10 +81,19 @@
inherit system;
# apply our own overlay, overriding/inserting our packages as defined in ./pkgs
overlays = [ self.overlays.default ];
overlays = [
self.overlays.default
nix-vm-test.overlays.default
];
};
in
{
packages.package-deb = pkgs.callPackage ./pkgs/package-deb.nix {
rosenpass = pkgs.pkgsStatic.rosenpass;
};
packages.package-rpm = pkgs.callPackage ./pkgs/package-rpm.nix {
rosenpass = pkgs.pkgsStatic.rosenpass;
};
#
### Reading materials ###
@@ -109,16 +122,37 @@
proverif-patched
];
};
# TODO: Write this as a patched version of the default environment
devShells.fullEnv = pkgs.mkShell {
inherit (pkgs.proof-proverif) CRYPTOVERIF_LIB;
inputsFrom = [ pkgs.rosenpass ];
nativeBuildInputs = with pkgs; [
cargo-audit
cargo-release
cargo-msrv
rustfmt
nodePackages.prettier
nushell # for the .ci/gen-workflow-files.nu script
proverif-patched
inputs.fenix.packages.${system}.complete.toolchain
pkgs.cargo-llvm-cov
pkgs.grcov
];
};
devShells.coverage = pkgs.mkShell {
inputsFrom = [ pkgs.rosenpass ];
nativeBuildInputs = [
inputs.fenix.packages.${system}.complete.toolchain
pkgs.cargo-llvm-cov
pkgs.grcov
];
};
checks = {
systemd-rosenpass = pkgs.testers.runNixOSTest ./tests/systemd/rosenpass.nix;
systemd-rp = pkgs.testers.runNixOSTest ./tests/systemd/rp.nix;
cargo-fmt = pkgs.runCommand "check-cargo-fmt"
{ inherit (self.devShells.${system}.default) nativeBuildInputs buildInputs; } ''
cargo fmt --manifest-path=${./.}/Cargo.toml --check --all && touch $out
@@ -131,7 +165,11 @@
{ nativeBuildInputs = [ pkgs.nodePackages.prettier ]; } ''
cd ${./.} && prettier --check . && touch $out
'';
};
} // pkgs.lib.optionalAttrs (system == "x86_64-linux") (import ./tests/legacy-distro-packaging.nix {
inherit pkgs;
rosenpass-deb = self.packages.${system}.package-deb;
rosenpass-rpm = self.packages.${system}.package-rpm;
});
formatter = pkgs.nixpkgs-fmt;
}))

View File

@@ -3,9 +3,10 @@ name = "rosenpass-fuzzing"
version = "0.0.1"
publish = false
edition = "2021"
rust-version = "1.77"
[features]
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"]
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux_all"]
[package.metadata]
cargo-fuzz = true

View File

@@ -4,7 +4,8 @@ extern crate rosenpass;
use libfuzzer_sys::fuzz_target;
use rosenpass_ciphers::aead;
use rosenpass_cipher_traits::primitives::Aead as _;
use rosenpass_ciphers::Aead;
#[derive(arbitrary::Arbitrary, Debug)]
pub struct Input {
@@ -17,7 +18,7 @@ pub struct Input {
fuzz_target!(|input: Input| {
let mut ciphertext = vec![0u8; input.plaintext.len() + 16];
aead::encrypt(
Aead.encrypt(
ciphertext.as_mut_slice(),
&input.key,
&input.nonce,

View File

@@ -4,6 +4,7 @@ extern crate rosenpass;
use libfuzzer_sys::fuzz_target;
use rosenpass_cipher_traits::primitives::KeyedHashTo;
use rosenpass_ciphers::subtle::blake2b;
use rosenpass_to::To;
@@ -16,5 +17,7 @@ pub struct Blake2b {
fuzz_target!(|input: Blake2b| {
let mut out = [0u8; 32];
blake2b::hash(&input.key, &input.data).to(&mut out).unwrap();
blake2b::Blake2b::keyed_hash_to(&input.key, &input.data)
.to(&mut out)
.unwrap();
});

View File

@@ -4,8 +4,8 @@ extern crate rosenpass;
use libfuzzer_sys::fuzz_target;
use rosenpass::protocol::CryptoServer;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem;
use rosenpass_cipher_traits::primitives::Kem;
use rosenpass_ciphers::StaticKem;
use rosenpass_secret_memory::policy::*;
use rosenpass_secret_memory::{PublicBox, Secret};
use std::sync::Once;

View File

@@ -4,8 +4,8 @@ extern crate rosenpass;
use libfuzzer_sys::fuzz_target;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::EphemeralKem;
use rosenpass_cipher_traits::primitives::Kem;
use rosenpass_ciphers::EphemeralKem;
#[derive(arbitrary::Arbitrary, Debug)]
pub struct Input {
@@ -16,5 +16,7 @@ fuzz_target!(|input: Input| {
let mut ciphertext = [0u8; EphemeralKem::CT_LEN];
let mut shared_secret = [0u8; EphemeralKem::SHK_LEN];
EphemeralKem::encaps(&mut shared_secret, &mut ciphertext, &input.pk).unwrap();
EphemeralKem
.encaps(&mut shared_secret, &mut ciphertext, &input.pk)
.unwrap();
});

View File

@@ -3,13 +3,13 @@ extern crate rosenpass;
use libfuzzer_sys::fuzz_target;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem;
use rosenpass_cipher_traits::primitives::Kem;
use rosenpass_ciphers::StaticKem;
fuzz_target!(|input: [u8; StaticKem::PK_LEN]| {
let mut ciphertext = [0u8; StaticKem::CT_LEN];
let mut shared_secret = [0u8; StaticKem::SHK_LEN];
// 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);
});

View File

@@ -8,6 +8,7 @@ description = "Rosenpass internal bindings to liboqs"
homepage = "https://rosenpass.eu/"
repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
rust-version = "1.77"
[dependencies]
rosenpass-cipher-traits = { workspace = true }

View File

@@ -2,11 +2,10 @@
/// Generate bindings to a liboqs-provided KEM
macro_rules! oqs_kem {
($name:ident) => { ::paste::paste!{
($name:ident, $algo_trait:path) => { ::paste::paste!{
#[doc = "Bindings for ::oqs_sys::kem::" [<"OQS_KEM" _ $name:snake>] "_*"]
mod [< $name:snake >] {
use rosenpass_cipher_traits::Kem;
use rosenpass_util::result::Guaranteed;
use rosenpass_cipher_traits::primitives::{Kem, KemError};
#[doc = "Bindings for ::oqs_sys::kem::" [<"OQS_KEM" _ $name:snake>] "_*"]
#[doc = ""]
@@ -14,7 +13,7 @@ macro_rules! oqs_kem {
#[doc = ""]
#[doc = "```rust"]
#[doc = "use std::borrow::{Borrow, BorrowMut};"]
#[doc = "use rosenpass_cipher_traits::Kem;"]
#[doc = "use rosenpass_cipher_traits::primitives::Kem;"]
#[doc = "use rosenpass_oqs::" $name:camel " as MyKem;"]
#[doc = "use rosenpass_secret_memory::{Secret, Public};"]
#[doc = ""]
@@ -23,21 +22,26 @@ macro_rules! oqs_kem {
#[doc = "// Recipient generates secret key, transfers pk to sender"]
#[doc = "let mut sk = Secret::<{ MyKem::SK_LEN }>::zero();"]
#[doc = "let mut pk = Public::<{ MyKem::PK_LEN }>::zero();"]
#[doc = "MyKem::keygen(sk.secret_mut(), pk.borrow_mut());"]
#[doc = "MyKem.keygen(sk.secret_mut(), &mut pk);"]
#[doc = ""]
#[doc = "// Sender generates ciphertext and local shared key, sends ciphertext to recipient"]
#[doc = "let mut shk_enc = Secret::<{ MyKem::SHK_LEN }>::zero();"]
#[doc = "let mut ct = Public::<{ MyKem::CT_LEN }>::zero();"]
#[doc = "MyKem::encaps(shk_enc.secret_mut(), ct.borrow_mut(), pk.borrow());"]
#[doc = "MyKem.encaps(shk_enc.secret_mut(), &mut ct, &pk);"]
#[doc = ""]
#[doc = "// Recipient decapsulates ciphertext"]
#[doc = "let mut shk_dec = Secret::<{ MyKem::SHK_LEN }>::zero();"]
#[doc = "MyKem::decaps(shk_dec.secret_mut(), sk.secret(), ct.borrow());"]
#[doc = "MyKem.decaps(shk_dec.secret_mut(), sk.secret_mut(), &ct);"]
#[doc = ""]
#[doc = "// Both parties end up with the same shared key"]
#[doc = "assert!(rosenpass_constant_time::compare(shk_enc.secret_mut(), shk_dec.secret_mut()) == 0);"]
#[doc = "assert!(rosenpass_constant_time::compare(shk_enc.secret(), shk_dec.secret()) == 0);"]
#[doc = "```"]
pub enum [< $name:camel >] {}
pub struct [< $name:camel >];
pub const SK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_secret_key >] as usize;
pub const PK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_public_key >] as usize;
pub const CT_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_ciphertext >] as usize;
pub const SHK_LEN: usize = ::oqs_sys::kem::[<OQS_KEM _ $name:snake _ length_shared_secret >] as usize;
/// # Panic & Safety
///
@@ -51,17 +55,8 @@ macro_rules! oqs_kem {
/// 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);
impl Kem<SK_LEN, PK_LEN, CT_LEN, SHK_LEN> for [< $name:camel >] {
fn keygen(&self, sk: &mut [u8; SK_LEN], pk: &mut [u8; PK_LEN]) -> Result<(), KemError> {
unsafe {
oqs_call!(
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ keypair >],
@@ -73,10 +68,7 @@ macro_rules! oqs_kem {
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);
fn encaps(&self, shk: &mut [u8; SHK_LEN], ct: &mut [u8; CT_LEN], pk: &[u8; PK_LEN]) -> Result<(), KemError> {
unsafe {
oqs_call!(
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ encaps >],
@@ -89,10 +81,7 @@ macro_rules! oqs_kem {
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);
fn decaps(&self, shk: &mut [u8; SHK_LEN], sk: &[u8; SK_LEN], ct: &[u8; CT_LEN]) -> Result<(), KemError> {
unsafe {
oqs_call!(
::oqs_sys::kem::[< OQS_KEM _ $name:snake _ decaps >],
@@ -105,9 +94,16 @@ macro_rules! oqs_kem {
Ok(())
}
}
}
impl Default for [< $name:camel >] {
fn default() -> Self {
Self
}
}
impl $algo_trait for [< $name:camel >] {}
pub use [< $name:snake >] :: [< $name:camel >];
}}
}

View File

@@ -22,5 +22,8 @@ macro_rules! oqs_call {
#[macro_use]
mod kem_macro;
oqs_kem!(kyber_512);
oqs_kem!(classic_mceliece_460896);
oqs_kem!(kyber_512, rosenpass_cipher_traits::algorithms::KemKyber512);
oqs_kem!(
classic_mceliece_460896,
rosenpass_cipher_traits::algorithms::KemClassicMceliece460896
);

View File

@@ -2,8 +2,8 @@
template: rosenpass
title: Rosenpass
author:
- Karolin Varner = Independent Researcher
- Benjamin Lipp = Max Planck Institute for Security and Privacy (MPI-SP)
- Karolin Varner = Rosenpass e.V., Max Planck Institute for Security and Privacy (MPI-SP)
- Benjamin Lipp = Rosenpass e.V., Max Planck Institute for Security and Privacy (MPI-SP)
- Wanja Zaeske
- Lisa Schmidt = {Scientific Illustrator \\url{mullana.de}}
- Prabhpreet Dua
@@ -383,9 +383,18 @@ fn load_biscuit(nct) {
"biscuit additional data",
spkr, sidi, sidr);
let pt : Biscuit = XAEAD::dec(k, n, ct, ad);
// Find the peer and apply retransmission protection
lookup_peer(pt.peerid);
assert(pt.biscuit_no <= peer.biscuit_used);
// In December 2024, the InitConf retransmission mechanisim was redesigned
// in a backwards-compatible way. See the changelog.
//
// -- 2024-11-30, Karolin Varner
if (protocol_version!(< "0.3.0")) {
// Ensure that the biscuit is used only once
assert(pt.biscuit_no <= peer.biscuit_used);
}
// Restore the chaining key
ck ← pt.ck;
@@ -501,7 +510,7 @@ LAST_UNDER_LOAD_WINDOW = 1 //seconds
The initiator deals with packet loss by storing the messages it sends to the responder and retransmitting them in randomized, exponentially increasing intervals until they get a response. Receiving RespHello terminates retransmission of InitHello. A Data or EmptyData message serves as acknowledgement of receiving InitConf and terminates its retransmission.
The responder does not need to do anything special to handle RespHello retransmission if the RespHello package is lost, the initiator retransmits InitHello and the responder can generate another RespHello package from that. InitConf retransmission needs to be handled specifically in the responder code because accepting an InitConf retransmission would reset the live session including the nonce counter, which would cause nonce reuse. Implementations must detect the case that `biscuit_no = biscuit_used` in ICR5, skip execution of ICR6 and ICR7, and just transmit another EmptyData package to confirm that the initiator can stop transmitting InitConf.
The responder uses less complex form of the same mechanism: The responder never retransmits RespHello, instead the responder generates a new RespHello message if InitHello is retransmitted. Responder confirmation messages of completed handshake (EmptyData) messages are retransmitted by storing the most recent InitConf messages (or their hashes) and caching the associated EmptyData messages. Through this cache, InitConf retransmission is detected and the associated EmptyData message is retransmitted.
### Interaction with cookie reply system
@@ -515,6 +524,76 @@ When the responder is under load and it recieves an InitConf message, the messag
# Changelog
### 0.3.x
#### 2024-10-30 InitConf retransmission updates
\vspace{0.5em}
Author: Karolin Varner
Issue: [#331](https://github.com/rosenpass/rosenpass/issues/331)
PR: [#513](https://github.com/rosenpass/rosenpass/pull/513)
\vspace{0.5em}
We redesign the InitConf retransmission mechanism to use a hash table. This avoids the need for the InitConf handling code to account for InitConf retransmission specifically and moves the retransmission logic into less-sensitive code.
Previously, we would specifically account for InitConf retransmission in the InitConf handling code by checking the biscuit number: If the biscuit number was higher than any previously seen biscuit number, then this must be a new key-exchange being completed; if the biscuit number was exactly the highest seen biscuit number, then the InitConf message is interpreted as an InitConf retransmission; in this case, an entirely new EmptyData (responder confirmation) message was generated as confirmation that InitConf has been received and that the initiator can now cease opportunistic retransmission of InitConf.
This mechanism was a bit brittle; even leading to a very minor but still relevant security issue, necessitating the release of Rosenpass maintenance version 0.2.2 with a [fix for the problem](https://github.com/rosenpass/rosenpass/pull/329). We had processed the InitConf message, correctly identifying that InitConf was a retransmission, but we failed to pass this information on to the rest of the code base, leading to double emission of the same "hey, we have a new cryptographic session key" even if the `outfile` option was used to integrate Rosenpass into some external application. If this event was used anywhere to reset a nonce, then this could have led to a nonce-misuse, although for the use with WireGuard this is not an issue.
By removing all retransmission handling code from the cryptographic protocol, we are taking structural measures to exclude the possibilities of similar issues.
- In section "Dealing With Package Loss" we replace
\begin{quote}
The responder does not need to do anything special to handle RespHello retransmission if the RespHello package is lost, the initiator retransmits InitHello and the responder can generate another RespHello package from that. InitConf retransmission needs to be handled specifically in the responder code because accepting an InitConf retransmission would reset the live session including the nonce counter, which would cause nonce reuse. Implementations must detect the case that `biscuit_no = biscuit_used` in ICR5, skip execution of ICR6 and ICR7, and just transmit another EmptyData package to confirm that the initiator can stop transmitting InitConf.
\end{quote}
by
\begin{quote}
The responder uses less complex form of the same mechanism: The responder never retransmits RespHello, instead the responder generates a new RespHello message if InitHello is retransmitted. Responder confirmation messages of completed handshake (EmptyData) messages are retransmitted by storing the most recent InitConf messages (or their hashes) and caching the associated EmptyData messages. Through this cache, InitConf retransmission is detected and the associated EmptyData message is retransmitted.
\end{quote}
- In function `load_biscuit` we replace
``` {=tex}
\begin{quote}
\begin{minted}{pseudorust}
assert(pt.biscuit_no <= peer.biscuit_used);
\end{minted}
\end{quote}
```
by
``` {=tex}
\begin{quote}
\begin{minted}{pseudorust}
// In December 2024, the InitConf retransmission mechanisim was redesigned
// in a backwards-compatible way. See the changelog.
//
// -- 2024-11-30, Karolin Varner
if (protocol_version!(< "0.3.0")) {
// Ensure that the biscuit is used only once
assert(pt.biscuit_no <= peer.biscuit_used);
}
\end{minted}
\end{quote}
```
#### 2024-04-16 Denial of Service Mitigation
\vspace{0.5em}
Author: Prabhpreet Dua
Issue: [#137](https://github.com/rosenpass/rosenpass/issues/137)
PR: [#142](https://github.com/rosenpass/rosenpass/pull/142)
\vspace{0.5em}
- Added denial of service mitigation using the WireGuard cookie mechanism
- Added section "Denial of Service Mitigation and Cookies", and modify "Dealing with Packet Loss" for DoS cookie mechanism
\printbibliography

9
pkgs/example.toml Normal file
View File

@@ -0,0 +1,9 @@
dev = "rp-example"
ip = "fc00::1/64"
listen = "[::]:51821"
private_keys_dir = "/run/credentials/rp@example.service"
verbose = true
[[peers]]
public_keys_dir = "/etc/rosenpass/example/peers/client"
allowed_ips = "fc00::2"

30
pkgs/package-deb.nix Normal file
View File

@@ -0,0 +1,30 @@
{ runCommand, dpkg, rosenpass }:
let
inherit (rosenpass) version;
in
runCommand "rosenpass-${version}.deb" { } ''
mkdir -p packageroot/DEBIAN
cat << EOF > packageroot/DEBIAN/control
Package: rosenpass
Version: ${version}
Architecture: all
Maintainer: Jacek Galowicz <jacek@galowicz.de>
Depends:
Description: Post-quantum-secure VPN tool Rosenpass
Rosenpass is a post-quantum-secure VPN
that uses WireGuard to transport the actual data.
EOF
mkdir -p packageroot/usr/bin
install -m755 -t packageroot/usr/bin ${rosenpass}/bin/*
mkdir -p packageroot/etc/rosenpass
cp -r ${rosenpass}/lib/systemd packageroot/etc/
cp ${./example.toml} packageroot/etc/rosenpass/example.toml
${dpkg}/bin/dpkg --build packageroot $out
''

57
pkgs/package-rpm.nix Normal file
View File

@@ -0,0 +1,57 @@
{ lib, system, runCommand, rosenpass, rpm }:
let
splitVersion = lib.strings.splitString "-" rosenpass.version;
version = builtins.head splitVersion;
release =
if builtins.length splitVersion != 2
then "release"
else builtins.elemAt splitVersion 1;
arch = builtins.head (builtins.split "-" system);
in
runCommand "rosenpass-${version}.rpm" { } ''
mkdir -p rpmbuild/SPECS
cat << EOF > rpmbuild/SPECS/rosenpass.spec
Name: rosenpass
Release: ${release}
Version: ${version}
Summary: Post-quantum-secure VPN key exchange
License: Apache-2.0
%description
Post-quantum-secure VPN tool Rosenpass
Rosenpass is a post-quantum-secure VPN
that uses WireGuard to transport the actual data.
%files
/usr/bin/rosenpass
/usr/bin/rp
/etc/systemd/system/rosenpass.target
/etc/systemd/system/rosenpass@.service
/etc/systemd/system/rp@.service
/etc/rosenpass/example.toml
EOF
buildroot=rpmbuild/BUILDROOT/rosenpass-${version}-${release}.${arch}
mkdir -p $buildroot/usr/bin
install -m755 -t $buildroot/usr/bin ${rosenpass}/bin/*
mkdir -p $buildroot/etc/rosenpass
cp -r ${rosenpass}/lib/systemd $buildroot/etc/
chmod -R 744 $buildroot/etc/systemd
cp ${./example.toml} $buildroot/etc/rosenpass/example.toml
export HOME=/build
mkdir -p /build/tmp
ls -R rpmbuild
${rpm}/bin/rpmbuild \
-bb \
--dbpath=$HOME \
--define "_tmppath /build/tmp" \
rpmbuild/SPECS/rosenpass.spec
cp rpmbuild/RPMS/${arch}/rosenpass*.rpm $out
''

View File

@@ -20,7 +20,7 @@ in
runCommandNoCC "lace-result" { } ''
mkdir {bin,$out}
tar -cvf $out/rosenpass-${stdenvNoCC.hostPlatform.system}-${version}.tar \
-C ${package} bin/rosenpass \
-C ${package} bin/rosenpass lib/systemd \
-C ${rp} bin/rp
cp ${oci-image} \
$out/rosenpass-oci-image-${stdenvNoCC.hostPlatform.system}-${version}.tar.gz

View File

@@ -12,6 +12,8 @@ let
extensions = [
"lock"
"rs"
"service"
"target"
"toml"
];
# Files to explicitly include
@@ -55,6 +57,7 @@ rustPlatform.buildRustPackage {
outputHashes = {
"memsec-0.6.3" = "sha256-4ri+IEqLd77cLcul3lZrmpDKj4cwuYJ8oPRAiQNGeLw=";
"uds-0.4.2" = "sha256-qlxr/iJt2AV4WryePIvqm/8/MK/iqtzegztNliR93W8=";
"libcrux-blake2-0.0.3-pre" = "sha256-0CLjuzwJqGooiODOHf5D8Hc8ClcG/XcGvVGyOVnLmJY=";
};
};
@@ -69,6 +72,13 @@ rustPlatform.buildRustPackage {
hardeningDisable = lib.optional isStatic "fortify";
postInstall = ''
mkdir -p $out/lib/systemd/system
install systemd/rosenpass@.service $out/lib/systemd/system
install systemd/rp@.service $out/lib/systemd/system
install systemd/rosenpass.target $out/lib/systemd/system
'';
meta = {
inherit (cargoToml.package) description homepage;
license = with lib.licenses; [ mit asl20 ];

View File

@@ -23,6 +23,12 @@ rosenpass help
Follow [quick start instructions](https://rosenpass.eu/#start) to get a VPN up and running.
## Contributing
Contributions are generally welcome. Join our [Matrix Chat](https://matrix.to/#/#rosenpass:matrix.org) if you are looking for guidance on how to contribute or for people to collaborate with.
We also have a as of now, very minimal [contributors guide](CONTRIBUTING.md).
## Software architecture
The [rosenpass tool](./src/) is written in Rust and uses liboqs[^liboqs]. The tool establishes a symmetric key and provides it to WireGuard. Since it supplies WireGuard with key through the PSK feature using Rosenpass+WireGuard is cryptographically no less secure than using WireGuard on its own ("hybrid security"). Rosenpass refreshes the symmetric key every two minutes.
@@ -72,6 +78,15 @@ Rosenpass is packaged for more and more distributions, maybe also for the distri
[![Packaging status](https://repology.org/badge/vertical-allrepos/rosenpass.svg)](https://repology.org/project/rosenpass/versions)
## Docker Images
Rosenpass is also available as prebuilt Docker images:
- [`ghcr.io/rosenpass/rosenpass`](https://github.com/rosenpass/rosenpass/pkgs/container/rosenpass)
- [`ghcr.io/rosenpass/rp`](https://github.com/rosenpass/rosenpass/pkgs/container/rp)
For details on how to use these images, refer to the [Docker usage guide](docker/USAGE.md).
# Mirrors
Don't want to use GitHub or only have an IPv6 connection? Rosenpass has set up two mirrors for this:

View File

@@ -8,6 +8,7 @@ description = "Build post-quantum-secure VPNs with WireGuard!"
homepage = "https://rosenpass.eu/"
repository = "https://github.com/rosenpass/rosenpass"
readme = "readme.md"
rust-version = "1.77"
[[bin]]
name = "rosenpass"
@@ -26,6 +27,14 @@ required-features = ["experiment_api", "internal_testing"]
name = "api-integration-tests-api-setup"
required-features = ["experiment_api", "internal_testing"]
[[test]]
name = "gen-ipc-msg-types"
required-features = [
"experiment_api",
"internal_testing",
"internal_bin_gen_ipc_msg_types",
]
[[bench]]
name = "handshake"
harness = false
@@ -62,6 +71,7 @@ heck = { workspace = true, optional = true }
command-fds = { workspace = true, optional = true }
rustix = { workspace = true, optional = true }
uds = { workspace = true, optional = true, features = ["mio_1xx"] }
signal-hook = { workspace = true, optional = true }
[build-dependencies]
anyhow = { workspace = true }
@@ -76,9 +86,15 @@ tempfile = { workspace = true }
rustix = { workspace = true }
[features]
default = []
#default = ["experiment_libcrux_all"]
experiment_cookie_dos_mitigation = []
experiment_memfd_secret = ["rosenpass-wireguard-broker/experiment_memfd_secret"]
experiment_libcrux = ["rosenpass-ciphers/experiment_libcrux"]
experiment_libcrux_all = ["rosenpass-ciphers/experiment_libcrux_all"]
experiment_libcrux_blake2 = ["rosenpass-ciphers/experiment_libcrux_blake2"]
experiment_libcrux_chachapoly = [
"rosenpass-ciphers/experiment_libcrux_chachapoly",
]
experiment_libcrux_kyber = ["rosenpass-ciphers/experiment_libcrux_kyber"]
experiment_api = [
"hex-literal",
"uds",
@@ -87,5 +103,9 @@ experiment_api = [
"rosenpass-util/experiment_file_descriptor_passing",
"rosenpass-wireguard-broker/experiment_api",
]
internal_signal_handling_for_coverage_reports = ["signal-hook"]
internal_testing = []
internal_bin_gen_ipc_msg_types = ["hex", "heck"]
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }

View File

@@ -1,9 +1,11 @@
use anyhow::Result;
use rosenpass::protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, SPk, SSk, SymKey};
use rosenpass::protocol::{
CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, ProtocolVersion, SPk, SSk, SymKey,
};
use std::ops::DerefMut;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem;
use rosenpass_cipher_traits::primitives::Kem;
use rosenpass_ciphers::StaticKem;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets;
@@ -41,25 +43,33 @@ fn hs(ini: &mut CryptoServer, res: &mut CryptoServer) -> Result<()> {
fn keygen() -> Result<(SSk, SPk)> {
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
StaticKem::keygen(sk.secret_mut(), pk.deref_mut())?;
StaticKem.keygen(sk.secret_mut(), pk.deref_mut())?;
Ok((sk, pk))
}
fn make_server_pair() -> Result<(CryptoServer, CryptoServer)> {
fn make_server_pair(protocol_version: ProtocolVersion) -> Result<(CryptoServer, CryptoServer)> {
let psk = SymKey::random();
let ((ska, pka), (skb, pkb)) = (keygen()?, keygen()?);
let (mut a, mut b) = (
CryptoServer::new(ska, pka.clone()),
CryptoServer::new(skb, pkb.clone()),
);
a.add_peer(Some(psk.clone()), pkb)?;
b.add_peer(Some(psk), pka)?;
a.add_peer(Some(psk.clone()), pkb, protocol_version.clone())?;
b.add_peer(Some(psk), pka, protocol_version)?;
Ok((a, b))
}
fn criterion_benchmark(c: &mut Criterion) {
fn criterion_benchmark_v02(c: &mut Criterion) {
criterion_benchmark(c, ProtocolVersion::V02)
}
fn criterion_benchmark_v03(c: &mut Criterion) {
criterion_benchmark(c, ProtocolVersion::V03)
}
fn criterion_benchmark(c: &mut Criterion, protocol_version: ProtocolVersion) {
secret_policy_try_use_memfd_secrets();
let (mut a, mut b) = make_server_pair().unwrap();
let (mut a, mut b) = make_server_pair(protocol_version).unwrap();
c.bench_function("cca_secret_alloc", |bench| {
bench.iter(|| {
SSk::zero();
@@ -82,5 +92,6 @@ fn criterion_benchmark(c: &mut Criterion) {
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
criterion_group!(benches_v02, criterion_benchmark_v02);
criterion_group!(benches_v03, criterion_benchmark_v03);
criterion_main!(benches_v02, benches_v03);

View File

@@ -8,210 +8,250 @@ use super::{
};
pub trait ByteSliceRefExt: ByteSlice {
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker].
fn msg_type_maker(self) -> RefMaker<Self, RawMsgType> {
self.zk_ref_maker()
}
fn msg_type(self) -> anyhow::Result<Ref<Self, PingRequest>> {
self.zk_parse()
}
fn msg_type_from_prefix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
self.zk_parse_prefix()
}
fn msg_type_from_suffix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
self.zk_parse_suffix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker] and
/// [RefMakerRawMsgTypeExt::parse_request_msg_type]
fn request_msg_type(self) -> anyhow::Result<RequestMsgType> {
self.msg_type_maker().parse_request_msg_type()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker],
/// [RefMaker::from_prefix], and
/// [RefMakerRawMsgTypeExt::parse_request_msg_type].
fn request_msg_type_from_prefix(self) -> anyhow::Result<RequestMsgType> {
self.msg_type_maker()
.from_prefix()?
.parse_request_msg_type()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker],
/// [RefMaker::from_suffix], and
/// [RefMakerRawMsgTypeExt::parse_request_msg_type].
fn request_msg_type_from_suffix(self) -> anyhow::Result<RequestMsgType> {
self.msg_type_maker()
.from_suffix()?
.parse_request_msg_type()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker],
/// [RefMakerRawMsgTypeExt::parse_response_msg_type].
fn response_msg_type(self) -> anyhow::Result<ResponseMsgType> {
self.msg_type_maker().parse_response_msg_type()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker],
/// [RefMaker::from_prefix], and
/// [RefMakerRawMsgTypeExt::parse_response_msg_type].
fn response_msg_type_from_prefix(self) -> anyhow::Result<ResponseMsgType> {
self.msg_type_maker()
.from_prefix()?
.parse_response_msg_type()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker],
/// [RefMaker::from_suffix], and
/// [RefMakerRawMsgTypeExt::parse_response_msg_type].
fn response_msg_type_from_suffix(self) -> anyhow::Result<ResponseMsgType> {
self.msg_type_maker()
.from_suffix()?
.parse_response_msg_type()
}
/// Shorthand for the use of [RequestRef::parse] in chaining.
fn parse_request(self) -> anyhow::Result<RequestRef<Self>> {
RequestRef::parse(self)
}
/// Shorthand for the use of [RequestRef::parse_from_prefix] in chaining.
fn parse_request_from_prefix(self) -> anyhow::Result<RequestRef<Self>> {
RequestRef::parse_from_prefix(self)
}
/// Shorthand for the use of [RequestRef::parse_from_suffix] in chaining.
fn parse_request_from_suffix(self) -> anyhow::Result<RequestRef<Self>> {
RequestRef::parse_from_suffix(self)
}
/// Shorthand for the use of [ResponseRef::parse] in chaining.
fn parse_response(self) -> anyhow::Result<ResponseRef<Self>> {
ResponseRef::parse(self)
}
/// Shorthand for the use of [ResponseRef::parse_from_prefix] in chaining.
fn parse_response_from_prefix(self) -> anyhow::Result<ResponseRef<Self>> {
ResponseRef::parse_from_prefix(self)
}
/// Shorthand for the use of [ResponseRef::parse_from_suffix] in chaining.
fn parse_response_from_suffix(self) -> anyhow::Result<ResponseRef<Self>> {
ResponseRef::parse_from_suffix(self)
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker].
fn ping_request_maker(self) -> RefMaker<Self, PingRequest> {
self.zk_ref_maker()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker].
fn ping_request(self) -> anyhow::Result<Ref<Self, PingRequest>> {
self.zk_parse()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_prefix].
fn ping_request_from_prefix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
self.zk_parse_prefix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_suffix].
fn ping_request_from_suffix(self) -> anyhow::Result<Ref<Self, PingRequest>> {
self.zk_parse_suffix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker].
fn ping_response_maker(self) -> RefMaker<Self, PingResponse> {
self.zk_ref_maker()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse].
fn ping_response(self) -> anyhow::Result<Ref<Self, PingResponse>> {
self.zk_parse()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_prefix].
fn ping_response_from_prefix(self) -> anyhow::Result<Ref<Self, PingResponse>> {
self.zk_parse_prefix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_suffix].
fn ping_response_from_suffix(self) -> anyhow::Result<Ref<Self, PingResponse>> {
self.zk_parse_suffix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse].
fn supply_keypair_request(self) -> anyhow::Result<Ref<Self, SupplyKeypairRequest>> {
self.zk_parse()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_prefix].
fn supply_keypair_request_from_prefix(self) -> anyhow::Result<Ref<Self, SupplyKeypairRequest>> {
self.zk_parse_prefix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_suffix].
fn supply_keypair_request_from_suffix(self) -> anyhow::Result<Ref<Self, SupplyKeypairRequest>> {
self.zk_parse_suffix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker].
fn supply_keypair_response_maker(self) -> RefMaker<Self, SupplyKeypairResponse> {
self.zk_ref_maker()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse].
fn supply_keypair_response(self) -> anyhow::Result<Ref<Self, SupplyKeypairResponse>> {
self.zk_parse()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_prefix].
fn supply_keypair_response_from_prefix(
self,
) -> anyhow::Result<Ref<Self, SupplyKeypairResponse>> {
self.zk_parse_prefix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_suffix].
fn supply_keypair_response_from_suffix(
self,
) -> anyhow::Result<Ref<Self, SupplyKeypairResponse>> {
self.zk_parse_suffix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse].
fn add_listen_socket_request(self) -> anyhow::Result<Ref<Self, super::AddListenSocketRequest>> {
self.zk_parse()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_prefix].
fn add_listen_socket_request_from_prefix(
self,
) -> anyhow::Result<Ref<Self, super::AddListenSocketRequest>> {
self.zk_parse_prefix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_suffix].
fn add_listen_socket_request_from_suffix(
self,
) -> anyhow::Result<Ref<Self, super::AddListenSocketRequest>> {
self.zk_parse_suffix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker].
fn add_listen_socket_response_maker(self) -> RefMaker<Self, super::AddListenSocketResponse> {
self.zk_ref_maker()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse].
fn add_listen_socket_response(
self,
) -> anyhow::Result<Ref<Self, super::AddListenSocketResponse>> {
self.zk_parse()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_prefix].
fn add_listen_socket_response_from_prefix(
self,
) -> anyhow::Result<Ref<Self, super::AddListenSocketResponse>> {
self.zk_parse_prefix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_suffix].
fn add_listen_socket_response_from_suffix(
self,
) -> anyhow::Result<Ref<Self, super::AddListenSocketResponse>> {
self.zk_parse_suffix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse].
fn add_psk_broker_request(self) -> anyhow::Result<Ref<Self, super::AddPskBrokerRequest>> {
self.zk_parse()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_prefix].
fn add_psk_broker_request_from_prefix(
self,
) -> anyhow::Result<Ref<Self, super::AddPskBrokerRequest>> {
self.zk_parse_prefix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_suffix].
fn add_psk_broker_request_from_suffix(
self,
) -> anyhow::Result<Ref<Self, super::AddPskBrokerRequest>> {
self.zk_parse_suffix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_ref_maker].
fn add_psk_broker_response_maker(self) -> RefMaker<Self, super::AddPskBrokerResponse> {
self.zk_ref_maker()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse].
fn add_psk_broker_response(self) -> anyhow::Result<Ref<Self, super::AddPskBrokerResponse>> {
self.zk_parse()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_prefix].
fn add_psk_broker_response_from_prefix(
self,
) -> anyhow::Result<Ref<Self, super::AddPskBrokerResponse>> {
self.zk_parse_prefix()
}
/// Shorthand for the typed use of [ZerocopySliceExt::zk_parse_suffix].
fn add_psk_broker_response_from_suffix(
self,
) -> anyhow::Result<Ref<Self, super::AddPskBrokerResponse>> {

View File

@@ -4,16 +4,41 @@ use rosenpass_util::zerocopy::RefMaker;
use super::RawMsgType;
/// Trait implemented by all the Rosenpass API message types.
///
/// Implemented by the message as including the message envelope; e.g.
/// [crate::api::PingRequest] but not by [crate::api::PingRequestPayload].
pub trait Message {
/// The payload this API message contains. E.g. this is [crate::api::PingRequestPayload] for [[crate::api::PingRequest].
type Payload;
/// Either [crate::api::RequestMsgType] or [crate::api::ResponseMsgType]
type MessageClass: Into<RawMsgType>;
/// The specific message type in the [Self::MessageClass].
/// E.g. this is [crate::api::RequestMsgType::Ping] for [crate::api::PingRequest]
const MESSAGE_TYPE: Self::MessageClass;
/// Wraps the payload into the envelope
///
/// # Examples
///
/// See [crate::api::PingRequest::from_payload]
fn from_payload(payload: Self::Payload) -> Self;
/// Initialize the message;
/// just sets the message type [crate::api::Envelope::msg_type].
///
/// # Examples
///
/// See [crate::api::PingRequest::init]
fn init(&mut self);
/// Initialize the message from a raw buffer: Zeroize the buffer and then call [Self::init].
///
/// # Examples
///
/// See [crate::api::PingRequest::setup]
fn setup<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self>>;
}
/// Additional convenience functions for working with [rosenpass_util::zerocopy::RefMaker]
pub trait ZerocopyResponseMakerSetupMessageExt<B, T> {
fn setup_msg(self) -> anyhow::Result<Ref<B, T>>;
}
@@ -23,6 +48,27 @@ where
B: ByteSliceMut,
T: Message,
{
/// Initialize the message using [Message::setup].
///
/// # Examples
///
/// ```
/// use rosenpass::api::{
/// PingRequest, ZerocopyResponseMakerSetupMessageExt, PING_REQUEST,
/// };
/// use rosenpass_util::zerocopy::RefMaker;
/// use std::mem::size_of;
///
/// let mut buf = [0u8; { size_of::<PingRequest>() }];
///
/// let rm = RefMaker::<&mut [u8], PingRequest>::new(&mut buf);
/// let msg: zerocopy::Ref<_, PingRequest> = rm.setup_msg()?;
///
/// let t = msg.msg_type; // Deal with unaligned read
/// assert_eq!(t, PING_REQUEST);
///
/// Ok::<(), anyhow::Error>(())
/// ```
fn setup_msg(self) -> anyhow::Result<Ref<B, T>> {
T::setup(self.into_buf())
}

View File

@@ -35,10 +35,15 @@ const ADD_PSK_BROKER_REQUEST: RawMsgType =
const ADD_PSK_BROKER_RESPONSE: RawMsgType =
RawMsgType::from_le_bytes(hex!("bd25 e418 ffb0 6930 248b 217e 2fae e353"));
/// Message properties global to the message type
pub trait MessageAttributes {
/// Get the size of the message
///
/// # Exampleds
fn message_size(&self) -> usize;
}
/// API request message types as an enum
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum RequestMsgType {
Ping,
@@ -47,6 +52,7 @@ pub enum RequestMsgType {
AddPskBroker,
}
/// API response messages types as an enum
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum ResponseMsgType {
Ping,
@@ -131,8 +137,17 @@ impl From<ResponseMsgType> for RawMsgType {
}
}
/// Extension trait for [RawMsgType].
///
/// We are using an extension trait rather than just using methods
/// because [RawMsgType] is a type alias, so we can not define methods
/// on it.
pub trait RawMsgTypeExt {
/// Try to convert this to a [RequestMsgType]; alias for the appropriate [TryFrom]
/// implementation
fn into_request_msg_type(self) -> Result<RequestMsgType, RosenpassError>;
/// Try to convert this to a [ResponseMsgType]; alias for the appropriate [TryFrom]
/// implementation
fn into_response_msg_type(self) -> Result<ResponseMsgType, RosenpassError>;
}
@@ -146,8 +161,11 @@ impl RawMsgTypeExt for RawMsgType {
}
}
/// Extension trait for [rosenpass_util::zerocopy::RefMaker].
pub trait RefMakerRawMsgTypeExt {
/// Parse a request message type from bytes
fn parse_request_msg_type(self) -> anyhow::Result<RequestMsgType>;
/// Parse a response message type from bytes
fn parse_response_msg_type(self) -> anyhow::Result<ResponseMsgType>;
}

View File

@@ -1,3 +1,7 @@
//! Boring, repetitive code related to message parsing for the API.
//!
//! Most of this should be automatically generated though some derive macro at some point.
mod byte_slice_ext;
mod message_trait;
mod message_type;

View File

@@ -3,11 +3,14 @@ use zerocopy::{AsBytes, ByteSliceMut, FromBytes, FromZeroes, Ref};
use super::{Message, RawMsgType, RequestMsgType, ResponseMsgType};
/// Size required to fit any message in binary form
/// Size required to fit any request message in binary form
pub const MAX_REQUEST_LEN: usize = 2500; // TODO fix this
/// Size required to fit any response message in binary form
pub const MAX_RESPONSE_LEN: usize = 2500; // TODO fix this
/// Maximum number of file descriptors that can be sent in a request.
pub const MAX_REQUEST_FDS: usize = 2;
/// Message envelope for API messages
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct Envelope<M: AsBytes + FromBytes> {
@@ -17,9 +20,12 @@ pub struct Envelope<M: AsBytes + FromBytes> {
pub payload: M,
}
/// Message envelope for API requests
pub type RequestEnvelope<M> = Envelope<M>;
/// Message envelope for API responses
pub type ResponseEnvelope<M> = Envelope<M>;
#[allow(missing_docs)]
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct PingRequestPayload {
@@ -27,9 +33,11 @@ pub struct PingRequestPayload {
pub echo: [u8; 256],
}
#[allow(missing_docs)]
pub type PingRequest = RequestEnvelope<PingRequestPayload>;
impl PingRequest {
#[allow(missing_docs)]
pub fn new(echo: [u8; 256]) -> Self {
Self::from_payload(PingRequestPayload { echo })
}
@@ -58,6 +66,7 @@ impl Message for PingRequest {
}
}
#[allow(missing_docs)]
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct PingResponsePayload {
@@ -65,9 +74,11 @@ pub struct PingResponsePayload {
pub echo: [u8; 256],
}
#[allow(missing_docs)]
pub type PingResponse = ResponseEnvelope<PingResponsePayload>;
impl PingResponse {
#[allow(missing_docs)]
pub fn new(echo: [u8; 256]) -> Self {
Self::from_payload(PingResponsePayload { echo })
}
@@ -96,10 +107,12 @@ impl Message for PingResponse {
}
}
#[allow(missing_docs)]
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct SupplyKeypairRequestPayload {}
#[allow(missing_docs)]
pub type SupplyKeypairRequest = RequestEnvelope<SupplyKeypairRequestPayload>;
impl Default for SupplyKeypairRequest {
@@ -109,6 +122,7 @@ impl Default for SupplyKeypairRequest {
}
impl SupplyKeypairRequest {
#[allow(missing_docs)]
pub fn new() -> Self {
Self::from_payload(SupplyKeypairRequestPayload {})
}
@@ -137,25 +151,35 @@ impl Message for SupplyKeypairRequest {
}
}
#[allow(missing_docs)]
pub mod supply_keypair_response_status {
#[allow(missing_docs)]
pub const OK: u128 = 0;
#[allow(missing_docs)]
pub const KEYPAIR_ALREADY_SUPPLIED: u128 = 1;
// TODO: This is not actually part of the API. Remove.
/// TODO: This is not actually part of the API. Remove.
#[allow(missing_docs)]
pub const INTERNAL_ERROR: u128 = 2;
#[allow(missing_docs)]
pub const INVALID_REQUEST: u128 = 3;
/// TODO: Deprectaed, remove
#[allow(missing_docs)]
pub const IO_ERROR: u128 = 4;
}
#[allow(missing_docs)]
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct SupplyKeypairResponsePayload {
#[allow(missing_docs)]
pub status: u128,
}
#[allow(missing_docs)]
pub type SupplyKeypairResponse = ResponseEnvelope<SupplyKeypairResponsePayload>;
impl SupplyKeypairResponse {
#[allow(missing_docs)]
pub fn new(status: u128) -> Self {
Self::from_payload(SupplyKeypairResponsePayload { status })
}
@@ -184,10 +208,12 @@ impl Message for SupplyKeypairResponse {
}
}
#[allow(missing_docs)]
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct AddListenSocketRequestPayload {}
#[allow(missing_docs)]
pub type AddListenSocketRequest = RequestEnvelope<AddListenSocketRequestPayload>;
impl Default for AddListenSocketRequest {
@@ -197,6 +223,7 @@ impl Default for AddListenSocketRequest {
}
impl AddListenSocketRequest {
#[allow(missing_docs)]
pub fn new() -> Self {
Self::from_payload(AddListenSocketRequestPayload {})
}
@@ -225,21 +252,28 @@ impl Message for AddListenSocketRequest {
}
}
#[allow(missing_docs)]
pub mod add_listen_socket_response_status {
#[allow(missing_docs)]
pub const OK: u128 = 0;
#[allow(missing_docs)]
pub const INVALID_REQUEST: u128 = 1;
#[allow(missing_docs)]
pub const INTERNAL_ERROR: u128 = 2;
}
#[allow(missing_docs)]
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct AddListenSocketResponsePayload {
pub status: u128,
}
#[allow(missing_docs)]
pub type AddListenSocketResponse = ResponseEnvelope<AddListenSocketResponsePayload>;
impl AddListenSocketResponse {
#[allow(missing_docs)]
pub fn new(status: u128) -> Self {
Self::from_payload(AddListenSocketResponsePayload { status })
}
@@ -268,19 +302,23 @@ impl Message for AddListenSocketResponse {
}
}
#[allow(missing_docs)]
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct AddPskBrokerRequestPayload {}
#[allow(missing_docs)]
pub type AddPskBrokerRequest = RequestEnvelope<AddPskBrokerRequestPayload>;
impl Default for AddPskBrokerRequest {
#[allow(missing_docs)]
fn default() -> Self {
Self::new()
}
}
impl AddPskBrokerRequest {
#[allow(missing_docs)]
pub fn new() -> Self {
Self::from_payload(AddPskBrokerRequestPayload {})
}
@@ -309,21 +347,28 @@ impl Message for AddPskBrokerRequest {
}
}
#[allow(missing_docs)]
pub mod add_psk_broker_response_status {
#[allow(missing_docs)]
pub const OK: u128 = 0;
#[allow(missing_docs)]
pub const INVALID_REQUEST: u128 = 1;
#[allow(missing_docs)]
pub const INTERNAL_ERROR: u128 = 2;
}
#[allow(missing_docs)]
#[repr(packed)]
#[derive(Debug, Copy, Clone, Hash, AsBytes, FromBytes, FromZeroes, PartialEq, Eq)]
pub struct AddPskBrokerResponsePayload {
pub status: u128,
}
#[allow(missing_docs)]
pub type AddPskBrokerResponse = ResponseEnvelope<AddPskBrokerResponsePayload>;
impl AddPskBrokerResponse {
#[allow(missing_docs)]
pub fn new(status: u128) -> Self {
Self::from_payload(AddPskBrokerResponsePayload { status })
}

View File

@@ -4,24 +4,63 @@ use zerocopy::{ByteSlice, ByteSliceMut, Ref};
use super::{ByteSliceRefExt, MessageAttributes, PingRequest, RequestMsgType};
/// Helper for producing API message request references, [RequestRef].
///
/// This is to [RequestRef] as [rosenpass_util::zerocopy::RefMaker] is to
/// [zerocopy::Ref].
struct RequestRefMaker<B> {
buf: B,
msg_type: RequestMsgType,
}
impl<B: ByteSlice> RequestRef<B> {
/// Produce a [RequestRef] from a raw message buffer,
/// reading the type from the buffer
///
/// # Examples
///
/// ```
/// use zerocopy::AsBytes;
///
/// use rosenpass::api::{PingRequest, RequestRef, RequestMsgType};
///
/// let msg = PingRequest::new([0u8; 256]);
///
/// // TODO: HEISENBUG: This is necessary for some reason to make the rest of the example work
/// let typ = msg.msg_type;
/// assert_eq!(typ, rosenpass::api::PING_REQUEST);
///
/// let buf = msg.as_bytes();
/// let msg_ref = RequestRef::parse(buf)?;
/// assert!(matches!(msg_ref, RequestRef::Ping(_)));
///
/// assert_eq!(msg_ref.message_type(), RequestMsgType::Ping);
///
/// assert!(std::ptr::eq(buf, msg_ref.bytes()));
///
/// Ok::<(), anyhow::Error>(())
/// ```
pub fn parse(buf: B) -> anyhow::Result<Self> {
RequestRefMaker::new(buf)?.parse()
}
/// Produce a [ResponseRef] from the prefix of a raw message buffer,
/// reading the type from the buffer.
pub fn parse_from_prefix(buf: B) -> anyhow::Result<Self> {
RequestRefMaker::new(buf)?.from_prefix()?.parse()
}
/// Produce a [ResponseRef] from the prefix of a raw message buffer,
/// reading the type from the buffer.
pub fn parse_from_suffix(buf: B) -> anyhow::Result<Self> {
RequestRefMaker::new(buf)?.from_suffix()?.parse()
}
/// Get the message type [Self] contains
///
/// # Examples
///
/// See [Self::parse]
pub fn message_type(&self) -> RequestMsgType {
match self {
Self::Ping(_) => RequestMsgType::Ping,
@@ -110,6 +149,7 @@ impl<B: ByteSlice> RequestRefMaker<B> {
}
}
/// Reference to a API message response, typed as an enum.
pub enum RequestRef<B> {
Ping(Ref<B, PingRequest>),
SupplyKeypair(Ref<B, super::SupplyKeypairRequest>),
@@ -121,6 +161,11 @@ impl<B> RequestRef<B>
where
B: ByteSlice,
{
/// Access the byte data of this reference
///
/// # Examples
///
/// See [Self::parse].
pub fn bytes(&self) -> &[u8] {
match self {
Self::Ping(r) => r.bytes(),
@@ -135,6 +180,7 @@ impl<B> RequestRef<B>
where
B: ByteSliceMut,
{
/// Access the byte data of this reference; mutably
pub fn bytes_mut(&mut self) -> &[u8] {
match self {
Self::Ping(r) => r.bytes_mut(),

View File

@@ -6,23 +6,29 @@ use zerocopy::{ByteSlice, ByteSliceMut, Ref};
use super::{Message, PingRequest, PingResponse};
use super::{RequestRef, ResponseRef, ZerocopyResponseMakerSetupMessageExt};
/// Extension trait for [Message]s that are requests messages
pub trait RequestMsg: Sized + Message {
/// The response message belonging to this request message
type ResponseMsg: ResponseMsg;
/// Construct a response make for this particular message
fn zk_response_maker<B: ByteSlice>(buf: B) -> RefMaker<B, Self::ResponseMsg> {
buf.zk_ref_maker()
}
/// Setup a response maker (through [Message::setup]) for this request message type
fn setup_response<B: ByteSliceMut>(buf: B) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
Self::zk_response_maker(buf).setup_msg()
}
/// Setup a response maker from a buffer prefix (through [Message::setup]) for this request message type
fn setup_response_from_prefix<B: ByteSliceMut>(
buf: B,
) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
Self::zk_response_maker(buf).from_prefix()?.setup_msg()
}
/// Setup a response maker from a buffer suffix (through [Message::setup]) for this request message type
fn setup_response_from_suffix<B: ByteSliceMut>(
buf: B,
) -> anyhow::Result<Ref<B, Self::ResponseMsg>> {
@@ -30,6 +36,7 @@ pub trait RequestMsg: Sized + Message {
}
}
/// Extension trait for [Message]s that are response messages
pub trait ResponseMsg: Message {
type RequestMsg: RequestMsg;
}
@@ -66,20 +73,25 @@ impl ResponseMsg for super::AddPskBrokerResponse {
type RequestMsg = super::AddPskBrokerRequest;
}
/// Request and response for the [crate::api::RequestMsgType::Ping] message type
pub type PingPair<B1, B2> = (Ref<B1, PingRequest>, Ref<B2, PingResponse>);
/// Request and response for the [crate::api::RequestMsgType::SupplyKeypair] message type
pub type SupplyKeypairPair<B1, B2> = (
Ref<B1, super::SupplyKeypairRequest>,
Ref<B2, super::SupplyKeypairResponse>,
);
/// Request and response for the [crate::api::RequestMsgType::AddListenSocket] message type
pub type AddListenSocketPair<B1, B2> = (
Ref<B1, super::AddListenSocketRequest>,
Ref<B2, super::AddListenSocketResponse>,
);
/// Request and response for the [crate::api::RequestMsgType::AddPskBroker] message type
pub type AddPskBrokerPair<B1, B2> = (
Ref<B1, super::AddPskBrokerRequest>,
Ref<B2, super::AddPskBrokerResponse>,
);
/// A pair of references to messages; request and response each.
pub enum RequestResponsePair<B1, B2> {
Ping(PingPair<B1, B2>),
SupplyKeypair(SupplyKeypairPair<B1, B2>),
@@ -116,6 +128,7 @@ where
B1: ByteSlice,
B2: ByteSlice,
{
/// Returns a tuple to both the request and the response message
pub fn both(&self) -> (RequestRef<&[u8]>, ResponseRef<&[u8]>) {
match self {
Self::Ping((req, res)) => {
@@ -141,10 +154,12 @@ where
}
}
/// Returns the request message
pub fn request(&self) -> RequestRef<&[u8]> {
self.both().0
}
/// Returns the response message
pub fn response(&self) -> ResponseRef<&[u8]> {
self.both().1
}
@@ -155,6 +170,7 @@ where
B1: ByteSliceMut,
B2: ByteSliceMut,
{
/// Returns a mutable tuple to both the request and the response message
pub fn both_mut(&mut self) -> (RequestRef<&mut [u8]>, ResponseRef<&mut [u8]>) {
match self {
Self::Ping((req, res)) => {
@@ -180,10 +196,12 @@ where
}
}
/// Returns the request message, mutably
pub fn request_mut(&mut self) -> RequestRef<&mut [u8]> {
self.both_mut().0
}
/// Returns the response message, mutably
pub fn response_mut(&mut self) -> ResponseRef<&mut [u8]> {
self.both_mut().1
}

View File

@@ -5,24 +5,66 @@ use zerocopy::{ByteSlice, ByteSliceMut, Ref};
use super::{ByteSliceRefExt, MessageAttributes, PingResponse, ResponseMsgType};
/// Helper for producing API message response references, [ResponseRef].
///
/// This is to [ResponseRef] as [rosenpass_util::zerocopy::RefMaker] is to
/// [zerocopy::Ref].
struct ResponseRefMaker<B> {
/// Buffer we are referencing
buf: B,
/// Message type we are producing
msg_type: ResponseMsgType,
}
impl<B: ByteSlice> ResponseRef<B> {
/// Produce a [ResponseRef] from a raw message buffer,
/// reading the type from the buffer
///
/// # Examples
///
/// ```
/// use zerocopy::AsBytes;
///
/// use rosenpass::api::{PingResponse, ResponseRef, ResponseMsgType};
/// // Produce the original PingResponse
/// let msg = PingResponse::new([0u8; 256]);
///
/// // TODO: HEISENBUG: This is necessary for some reason to make the rest of the example work
/// let typ = msg.msg_type;
/// assert_eq!(typ, rosenpass::api::PING_RESPONSE);
///
/// // Parse as a message type
/// let buf = msg.as_bytes();
/// let msg_ref = ResponseRef::parse(buf)?;
/// assert!(matches!(msg_ref, ResponseRef::Ping(_)));
///
/// // Buffers and message types of course match what we expect
/// assert_eq!(msg_ref.message_type(), ResponseMsgType::Ping);
/// assert!(std::ptr::eq(buf, msg_ref.bytes()));
///
/// Ok::<(), anyhow::Error>(())
/// ```
pub fn parse(buf: B) -> anyhow::Result<Self> {
ResponseRefMaker::new(buf)?.parse()
}
/// Produce a [ResponseRef] from the prefix of a raw message buffer,
/// reading the type from the buffer.
pub fn parse_from_prefix(buf: B) -> anyhow::Result<Self> {
ResponseRefMaker::new(buf)?.from_prefix()?.parse()
}
/// Produce a [ResponseRef] from the prefix of a raw message buffer,
/// reading the type from the buffer.
pub fn parse_from_suffix(buf: B) -> anyhow::Result<Self> {
ResponseRefMaker::new(buf)?.from_suffix()?.parse()
}
/// Get the message type [Self] contains
///
/// # Examples
///
/// See [Self::parse]
pub fn message_type(&self) -> ResponseMsgType {
match self {
Self::Ping(_) => ResponseMsgType::Ping,
@@ -111,6 +153,7 @@ impl<B: ByteSlice> ResponseRefMaker<B> {
}
}
/// Reference to a API message response, typed.
pub enum ResponseRef<B> {
Ping(Ref<B, PingResponse>),
SupplyKeypair(Ref<B, super::SupplyKeypairResponse>),
@@ -122,6 +165,11 @@ impl<B> ResponseRef<B>
where
B: ByteSlice,
{
/// Access the byte data of this reference
///
/// # Examples
///
/// See [Self::parse].
pub fn bytes(&self) -> &[u8] {
match self {
Self::Ping(r) => r.bytes(),
@@ -136,6 +184,7 @@ impl<B> ResponseRef<B>
where
B: ByteSliceMut,
{
/// Access the byte data of this reference; mutably
pub fn bytes_mut(&mut self) -> &[u8] {
match self {
Self::Ping(r) => r.bytes_mut(),

View File

@@ -2,10 +2,21 @@ use super::{ByteSliceRefExt, Message, PingRequest, PingResponse, RequestRef, Req
use std::{collections::VecDeque, os::fd::OwnedFd};
use zerocopy::{ByteSlice, ByteSliceMut};
/// The rosenpass API implementation functions.
///
/// Implemented by [crate::api::ApiHandler].
///
/// # Examples
///
/// See the example of how to use the API in [crate::api].
pub trait Server {
/// This implements the handler for the [crate::api::RequestMsgType::Ping] API message
///
/// It merely takes a buffer and returns that same buffer.
///
/// # Examples
///
/// See the example of how to use the API in [crate::api].
fn ping(
&mut self,
req: &PingRequest,
@@ -54,6 +65,10 @@ pub trait Server {
/// The file descriptors for the keys need not be backed by a file on disk. You can supply a
/// [memfd](https://man.archlinux.org/man/memfd.2.en) or [memfd_secret](https://man.archlinux.org/man/memfd_secret.2.en)
/// backed file descriptor if the server keys are not backed by a file system file.
///
/// # Examples
///
/// See the example of how to use the API in [crate::api].
fn supply_keypair(
&mut self,
req: &super::SupplyKeypairRequest,
@@ -80,8 +95,13 @@ pub trait Server {
///
/// # Description
///
/// This endpoint allows you to supply a UDP listen socket; it will be used to perform
/// This endpoint allows you to supply a UDP listen socket; it will be used to perform key
/// key exchanges using the Rosenpass protocol.
/// cryptographic key exchanges via the Rosenpass protocol.
///
/// # Examples
///
/// See the example of how to use the API in [crate::api].
fn add_listen_socket(
&mut self,
req: &super::AddListenSocketRequest,
@@ -89,6 +109,31 @@ pub trait Server {
res: &mut super::AddListenSocketResponse,
) -> anyhow::Result<()>;
/// Supply a new PSK broker listen socket through file descriptor passing via the API
///
/// This implements the handler for the [crate::api::RequestMsgType::AddPskBroker] API message.
///
/// # File descriptors
///
/// 1. The listen socket; must be backed by a unix domain stream socket
///
/// # API Return Status
///
/// 1. [crate::api::add_psk_broker_response_status::OK] - Indicates success
/// 2. [crate::api::add_psk_broker_response_status::INVALID_REQUEST] Malformed request; could be:
/// - Missing file descriptors for public key
/// - Invalid file descriptor type
/// 3. [crate::api::add_psk_broker_response_status::INTERNAL_ERROR] Some other, non-fatal error
/// occured. Check the logs on log
///
/// # Description
///
/// This endpoint allows you to supply a UDP listen socket; it will be used to transmit
/// cryptographic keys exchanged to WireGuard.
///
/// # Examples
///
/// See the example of how to use the API in [crate::api].
fn add_psk_broker(
&mut self,
req: &super::AddPskBrokerRequest,
@@ -96,6 +141,11 @@ pub trait Server {
res: &mut super::AddPskBrokerResponse,
) -> anyhow::Result<()>;
/// Similar to [Self::handle_message], but takes a [RequestResponsePair]
/// instead of taking to separate byte buffers.
///
/// I.e. this function uses the explicit type tag encoded in [RequestResponsePair]
/// rather than reading the type tag from the request buffer.
fn dispatch<ReqBuf, ResBuf>(
&mut self,
p: &mut RequestResponsePair<ReqBuf, ResBuf>,
@@ -117,6 +167,14 @@ pub trait Server {
}
}
/// Called by [crate::api::mio::MioConnection] when a new API request was received.
///
/// The parameters are:
///
/// - `req` A buffer containing the request
/// - `res_fds` A list of file descriptors received during the API call (i.e. this is used
/// with unix socket file descriptor passing)
/// - `res` The buffer to store the response in.
fn handle_message<ReqBuf, ResBuf>(
&mut self,
req: ReqBuf,

View File

@@ -6,6 +6,7 @@ use crate::config::Rosenpass as RosenpassConfig;
use super::config::ApiConfig;
/// Additional command line arguments for the API
#[cfg(feature = "experiment_api")]
#[derive(Args, Debug)]
pub struct ApiCli {
@@ -27,10 +28,14 @@ pub struct ApiCli {
}
impl ApiCli {
/// Copy the parameters set here into the [RosenpassConfig].
/// Forwards to [Self::apply_to_api_config]:
pub fn apply_to_config(&self, cfg: &mut RosenpassConfig) -> anyhow::Result<()> {
self.apply_to_api_config(&mut cfg.api)
}
/// Fills the values from [ApiConfig::listen_path], [ApiConfig::listen_fd], and
/// [ApiConfig::stream_fd] with the values from [Self]
pub fn apply_to_api_config(&self, cfg: &mut ApiConfig) -> anyhow::Result<()> {
cfg.listen_path.extend_from_slice(&self.api_listen_path);
cfg.listen_fd.extend_from_slice(&self.api_listen_fd);

View File

@@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize};
use crate::app_server::AppServer;
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
/// Configuration options for the Rosenpass API
#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq)]
pub struct ApiConfig {
/// Where in the file-system to create the unix socket the rosenpass API will be listening for
/// connections on
@@ -23,6 +24,10 @@ pub struct ApiConfig {
}
impl ApiConfig {
/// Construct appropriate [UnixListener]s for each of the API
/// listeners and connections configured in [Self] and invoke
/// [AppServer::add_api_listener] for each to add them to the
/// [AppServer].
pub fn apply_to_app_server(&self, srv: &mut AppServer) -> anyhow::Result<()> {
for path in self.listen_path.iter() {
srv.add_api_listener(UnixListener::bind(path)?)?;
@@ -39,10 +44,12 @@ impl ApiConfig {
Ok(())
}
/// Sum of all the API sources configured in here
pub fn count_api_sources(&self) -> usize {
self.listen_path.len() + self.listen_fd.len() + self.stream_fd.len()
}
/// Checks if [Self::count_api_sources] is greater than zero
pub fn has_api_sources(&self) -> bool {
self.count_api_sources() > 0
}

View File

@@ -53,6 +53,9 @@ struct MioConnectionBuffers {
}
#[derive(Debug)]
/// Represents a single connection with an API client.
/// Includes the necessary buffers, the [ApiHandler],
/// and the [UnixStream] that is used for communication.
pub struct MioConnection {
io: UnixStream,
mio_token: mio::Token,
@@ -62,6 +65,8 @@ pub struct MioConnection {
}
impl MioConnection {
/// Construct a new [Self] for the given app server from the unix socket stream
/// to communicate on.
pub fn new(app_server: &mut AppServer, mut io: UnixStream) -> std::io::Result<Self> {
let mio_token = app_server.mio_token_dispenser.dispense();
app_server
@@ -88,6 +93,8 @@ impl MioConnection {
})
}
/// Checks if this unix stream should be closed by the enclosing
/// structure
pub fn should_close(&self) -> bool {
let exhausted = self
.buffers
@@ -97,22 +104,30 @@ impl MioConnection {
self.invalid_read && exhausted
}
/// Close and deregister this particular API connection
pub fn close(mut self, app_server: &mut AppServer) -> anyhow::Result<()> {
app_server.mio_poll.registry().deregister(&mut self.io)?;
Ok(())
}
/// Retrieve the mio token
pub fn mio_token(&self) -> mio::Token {
self.mio_token
}
}
/// We require references to both [MioConnection] and to the [AppServer] that contains it.
pub trait MioConnectionContext {
/// Reference to the [MioConnection] we are focusing on
fn mio_connection(&self) -> &MioConnection;
/// Reference to the [AppServer] that contains the [Self::mio_connection]
fn app_server(&self) -> &AppServer;
/// Mutable reference to the [MioConnection] we are focusing on
fn mio_connection_mut(&mut self) -> &mut MioConnection;
/// Mutable reference to the [AppServer] that contains the [Self::mio_connection]
fn app_server_mut(&mut self) -> &mut AppServer;
/// Called by [AppServer::poll] regularly to process any incoming (and outgoing) API messages
fn poll(&mut self) -> anyhow::Result<()> {
macro_rules! short {
($e:expr) => {
@@ -133,6 +148,7 @@ pub trait MioConnectionContext {
Ok(())
}
/// Called by [Self::poll] to process incoming messages
fn handle_incoming_message(&mut self) -> anyhow::Result<Option<()>> {
self.with_buffers_stolen(|this, bufs| {
// Acquire request & response. Caller is responsible to make sure
@@ -156,6 +172,7 @@ pub trait MioConnectionContext {
})
}
/// Called by [Self::poll] to write data in the send buffer to the unix stream
fn flush_write_buffer(&mut self) -> anyhow::Result<Option<()>> {
if self.write_buf_mut().exhausted() {
return Ok(Some(()));
@@ -194,6 +211,7 @@ pub trait MioConnectionContext {
}
}
/// Called by [Self::poll] to check for messages to receive
fn recv(&mut self) -> anyhow::Result<Option<()>> {
if !self.write_buf_mut().exhausted() || self.mio_connection().invalid_read {
return Ok(None);
@@ -257,10 +275,12 @@ pub trait MioConnectionContext {
}
}
/// Forwards to [MioConnection::mio_token]
fn mio_token(&self) -> mio::Token {
self.mio_connection().mio_token()
}
/// Forwards to [MioConnection::should_close]
fn should_close(&self) -> bool {
self.mio_connection().should_close()
}
@@ -299,6 +319,7 @@ trait MioConnectionContextPrivate: MioConnectionContext {
impl<T> MioConnectionContextPrivate for T where T: ?Sized + MioConnectionContext {}
/// Every [MioConnectionContext] is also a [ApiHandlerContext]
impl<T> ApiHandlerContext for T
where
T: ?Sized + MioConnectionContext,

View File

@@ -10,41 +10,59 @@ use crate::app_server::{AppServer, AppServerIoSource};
use super::{MioConnection, MioConnectionContext};
/// This is in essence a unix listener for API connections.
///
/// It contains a number of [UnixListener]s and the associated [MioConnection]s encapsulating [mio::net::UnixListener]s.
#[derive(Default, Debug)]
pub struct MioManager {
listeners: Vec<UnixListener>,
connections: Vec<Option<MioConnection>>,
}
/// Points at a particular source of IO events inside [MioManager]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum MioManagerIoSource {
// Source of IO events is the Nth unix socket listener (see [MioManager::listeners])
Listener(usize),
// Source of IO events is the Nth unix socket listener (see [MioManager::connections])
Connection(usize),
}
impl MioManager {
/// Construct an empty [Self]
pub fn new() -> Self {
Self::default()
}
}
/// Focus in on a particular [MioConnection] inside a [MioManager]
///
/// This is mainly used to implement [MioConnectionContext].
struct MioConnectionFocus<'a, T: ?Sized + MioManagerContext> {
/// [MioConnectionContext] to access the [MioManager] instance and [AppServer]
ctx: &'a mut T,
/// Index of the connection referenced to by [Self]
conn_idx: usize,
}
impl<'a, T: ?Sized + MioManagerContext> MioConnectionFocus<'a, T> {
/// Produce a MioConnectionContext from the [MioConnectionContext] and the connection index
fn new(ctx: &'a mut T, conn_idx: usize) -> Self {
Self { ctx, conn_idx }
}
}
pub trait MioManagerContext {
/// Reference to the [MioManager]
fn mio_manager(&self) -> &MioManager;
/// Reference to the [MioManager], mutably
fn mio_manager_mut(&mut self) -> &mut MioManager;
/// Reference to the [AppServer] this [MioManager] is associated with
fn app_server(&self) -> &AppServer;
/// Mutable reference to the [AppServer] this [MioManager] is associated with
fn app_server_mut(&mut self) -> &mut AppServer;
/// Add a new [UnixListener] to listen for API connections on
fn add_listener(&mut self, mut listener: UnixListener) -> io::Result<()> {
let srv = self.app_server_mut();
let mio_token = srv.mio_token_dispenser.dispense();
@@ -64,6 +82,7 @@ pub trait MioManagerContext {
Ok(())
}
/// Add a new connection to an API client
fn add_connection(&mut self, connection: UnixStream) -> io::Result<()> {
let connection = MioConnection::new(self.app_server_mut(), connection)?;
let mio_token = connection.mio_token();
@@ -84,6 +103,7 @@ pub trait MioManagerContext {
Ok(())
}
/// Poll a particular [MioManagerIoSource] in this [MioManager]
fn poll_particular(&mut self, io_source: MioManagerIoSource) -> anyhow::Result<()> {
use MioManagerIoSource as S;
match io_source {
@@ -93,12 +113,14 @@ pub trait MioManagerContext {
Ok(())
}
/// Check for new connections and poll all the [MioConnectionContext]s managed by [Self]
fn poll(&mut self) -> anyhow::Result<()> {
self.accept_connections()?;
self.poll_connections()?;
Ok(())
}
/// Check all the [UnixListener]s managed by this [MioManager] for new connections
fn accept_connections(&mut self) -> io::Result<()> {
for idx in 0..self.mio_manager_mut().listeners.len() {
self.accept_from(idx)?;
@@ -106,6 +128,7 @@ pub trait MioManagerContext {
Ok(())
}
/// Check a particular [UnixListener] managed by this for new connections.
fn accept_from(&mut self, idx: usize) -> io::Result<()> {
// Accept connection until the socket would block or returns another error
// TODO: This currently only adds connections--we eventually need the ability to remove
@@ -122,6 +145,7 @@ pub trait MioManagerContext {
Ok(())
}
/// Call [MioConnectionContext::poll] on all the [MioConnection]s in This
fn poll_connections(&mut self) -> anyhow::Result<()> {
for idx in 0..self.mio_manager().connections.len() {
self.poll_particular_connection(idx)?;
@@ -129,6 +153,7 @@ pub trait MioManagerContext {
Ok(())
}
/// Call [MioConnectionContext::poll] on a particular connection
fn poll_particular_connection(&mut self, idx: usize) -> anyhow::Result<()> {
if self.mio_manager().connections[idx].is_none() {
return Ok(());

View File

@@ -1,3 +1,11 @@
//! The bulk code relating to the Rosenpass unix socket API
//!
//! # Examples
//!
#![doc = "```ignore"]
#![doc = include_str!("../../tests/api-integration-tests-api-setup.rs")]
#![doc = "```"]
mod api_handler;
mod boilerplate;

View File

@@ -1,3 +1,5 @@
/// This contains the bulk of the rosenpass server IO handling code whereas
/// the actual cryptographic code lives in the [crate::protocol] module
use anyhow::bail;
use anyhow::Context;
@@ -40,6 +42,7 @@ use std::slice;
use std::time::Duration;
use std::time::Instant;
use crate::config::ProtocolVersion;
use crate::protocol::BuildCryptoServer;
use crate::protocol::HostIdentification;
use crate::{
@@ -49,33 +52,78 @@ use crate::{
use rosenpass_util::attempt;
use rosenpass_util::b64::B64Display;
const MAX_B64_KEY_SIZE: usize = 32 * 5 / 3;
const MAX_B64_PEER_ID_SIZE: usize = 32 * 5 / 3;
/// The maximum size of a base64 encoded symmetric key (estimate)
pub const MAX_B64_KEY_SIZE: usize = 32 * 5 / 3;
/// The maximum size of a base64 peer ID (estimate)
pub const MAX_B64_PEER_ID_SIZE: usize = 32 * 5 / 3;
/// The zero IPv4 address; this is generally used to tell network servers to choose any interface
/// when listening
const IPV4_ANY_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
/// The zero IPv6 address; this is generally used to tell network servers to choose any interface
/// when listening
const IPV6_ANY_ADDR: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
/// Ratio of blocking epoll(7) polls to non-blocking polls at which the rosenpass server
/// assumes it is under load (i.e. a DOS attack may be happening)
const UNDER_LOAD_RATIO: f64 = 0.5;
/// Period at which the DOS detection code updates whether there is an "under load" status
const DURATION_UPDATE_UNDER_LOAD_STATUS: Duration = Duration::from_millis(500);
const BROKER_ID_BYTES: usize = 8;
pub const BROKER_ID_BYTES: usize = 8;
fn ipv4_any_binding() -> SocketAddr {
/// IPv4 address that tells the network layer to listen on any interface
///
/// # Examples
///
/// See [AppServer::new].
pub fn ipv4_any_binding() -> SocketAddr {
// addr, port
SocketAddr::V4(SocketAddrV4::new(IPV4_ANY_ADDR, 0))
}
fn ipv6_any_binding() -> SocketAddr {
/// IPv6 address that tells the network layer to listen on any interface
///
/// # Examples
///
/// See [AppServer::new].
pub fn ipv6_any_binding() -> SocketAddr {
// addr, port, flowinfo, scope_id
SocketAddr::V6(SocketAddrV6::new(IPV6_ANY_ADDR, 0, 0, 0))
}
/// This is used to assign indices to MIO (epoll) event sources
#[derive(Debug, Default)]
pub struct MioTokenDispenser {
counter: usize,
pub counter: usize,
}
impl MioTokenDispenser {
/// Produces a single IO event source ID
///
/// # Examples
///
/// Use is quite straightforward:
///
/// ```
/// use rosenpass::app_server::MioTokenDispenser;
/// use mio::Token;
///
/// let mut dispenser = MioTokenDispenser {
/// counter: 0
/// };
///
/// let t1 = dispenser.dispense();
/// let t2 = dispenser.dispense();
///
/// assert_ne!(t1, t2);
///
/// // If you inspected the output, you would find that the dispenser is really just a counter.
/// // Though this is an implementation detail
/// assert_eq!(t1, Token(0));
/// assert_eq!(t2, Token(1));
/// ```
///
pub fn dispense(&mut self) -> Token {
let r = self.counter;
self.counter += 1;
@@ -83,42 +131,132 @@ impl MioTokenDispenser {
}
}
/// List of WireGuard brokers
///
/// Each WireGuard peer ([AppPeer]) is assigned a broker ([AppPeer::broker_peer]).
///
/// When a new has been exchanged, then its associated broker is called to transmit the key
/// to WireGuard (or to do something else).
///
/// Brokers live in [AppServer::brokers]. They are added/removed from the AppServer via
/// [AppServer::register_broker] and [AppServer::unregister_broker]. PSKs are distributed
/// to their respective brokers via [AppPeerPtr::set_psk].
///
/// Note that the entire broker system is an experimental feature; it is not up to the
/// same quality standards as the rest of the code. In particular, the use of [BROKER_ID_BYTES]
/// and a hash map is simultaneously overengineered and not really that useful for what it is
/// doing.
#[derive(Debug, Default)]
pub struct BrokerStore {
/// The collection of WireGuard brokers. See [Self].
pub store: HashMap<
Public<BROKER_ID_BYTES>,
Box<dyn WireguardBrokerMio<Error = anyhow::Error, MioError = anyhow::Error>>,
>,
}
/// Reference to a broker imbued with utility methods
#[derive(Debug, Clone)]
pub struct BrokerStorePtr(pub Public<BROKER_ID_BYTES>);
/// This is the broker configuration for a particular broker peer
#[derive(Debug)]
pub struct BrokerPeer {
/// Reference to the broker used for this particular peer
ptr: BrokerStorePtr,
/// Configuration for a WireGuard broker.
///
/// This is woefully overengineered and there is very little reason why the broker
/// configuration should not live in the particular WireGuard broker.
peer_cfg: Box<dyn WireguardBrokerCfg>,
}
impl BrokerPeer {
/// Create a broker peer
pub fn new(ptr: BrokerStorePtr, peer_cfg: Box<dyn WireguardBrokerCfg>) -> Self {
Self { ptr, peer_cfg }
}
/// Retrieve the pointer to WireGuard PSK broker used with this peer
pub fn ptr(&self) -> &BrokerStorePtr {
&self.ptr
}
}
/// IO/BusinessLogic information for a particular protocol peer.
///
/// There is a one-to-one correspondence between this struct and [crate::protocol::Peer];
/// whereas the struct in the protocol module stores information specific to the cryptographic layer,
/// this struct stores information IO information.
#[derive(Default, Debug)]
pub struct AppPeer {
/// If set, then [AppServer::output_key] will write generated output keys
/// to a file configured here and produce information on standard out to
/// notify the calling process that
pub outfile: Option<PathBuf>,
/// If this option is set, then [AppServer::output_key] will send generated output
/// keys to the broker configured here
pub broker_peer: Option<BrokerPeer>,
/// This is the network address configured for a particular peer at program start.
///
/// I.e. this is the address the rosenpass program will send [crate::msgs::InitHello]
/// packets to, trying to exchange a key.
///
/// Note that the remote peer may connect with another address. See [Self::current_endpoint].
pub initial_endpoint: Option<Endpoint>,
/// The network address currently used for a particular peer.
///
/// This is not necessarily the address that was configured at program start (see [Self::initial_endpoint]),
/// because the remote peer can initiate handshakes from an arbitrary network address.
///
/// If another peer successfully connects to this one from any address, then this field will
/// be updated to reflect which address this was.
pub current_endpoint: Option<Endpoint>,
}
impl AppPeer {
/// Retrieve the [Endpoint] associated with this peer.
///
/// I.e. the [Self::current_endpoint] if set and [Self::initial_endpoint] otherwise.
///
/// # Examples
///
/// ```
/// use rosenpass::app_server::{Endpoint, AppPeer};
/// use rosenpass_util::functional::run;
///
/// let mut peer = AppPeer {
/// outfile: None,
/// broker_peer: None,
/// initial_endpoint: Some(Endpoint::discovery_from_hostname("0.0.0.0:0".to_string())?),
/// current_endpoint: Some(Endpoint::discovery_from_hostname("0.0.0.0:1".to_string())?),
/// };
///
/// fn same(a: Option<&Endpoint>, b: Option<&Endpoint>) -> bool {
/// if a.is_none() && b.is_none() {
/// return true;
/// }
///
/// run(|| Some(std::ptr::eq(a?, b?)) )
/// .unwrap_or(a.is_some() == b.is_some())
/// }
///
/// assert!(same(peer.endpoint(), peer.current_endpoint.as_ref()));
///
/// let mut tmp = None;
/// std::mem::swap(&mut tmp, &mut peer.initial_endpoint);
/// assert!(same(peer.endpoint(), peer.current_endpoint.as_ref()));
///
/// std::mem::swap(&mut tmp, &mut peer.initial_endpoint);
/// std::mem::swap(&mut tmp, &mut peer.current_endpoint);
/// assert!(same(peer.endpoint(), peer.initial_endpoint.as_ref()));
///
/// peer.initial_endpoint = None;
/// peer.current_endpoint = None;
/// assert!(peer.endpoint().is_none());
///
/// Ok::<(), anyhow::Error>(())
/// ```
pub fn endpoint(&self) -> Option<&Endpoint> {
self.current_endpoint
.as_ref()
@@ -126,6 +264,8 @@ impl AppPeer {
}
}
/// No longer in use since we have the broker system (see [BrokerPeer])
/// TODO: Remove
#[derive(Default, Debug)]
pub struct WireguardOut {
// impl KeyOutput
@@ -134,12 +274,20 @@ pub struct WireguardOut {
pub extra_params: Vec<String>,
}
/// Used to indicate whether the rosenpass server is in normal operating
/// conditions or under load (i.e. a DOS attack is happening)
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DoSOperation {
UnderLoad,
Normal,
}
/// Integration test helpers for AppServer
///
/// TODO: Remove; this is no way to write integration tests
///
/// # Examples
///
/// See [AppServer]
#[derive(Debug, Builder)]
#[builder(pattern = "owned")]
pub struct AppServerTest {
@@ -151,41 +299,100 @@ pub struct AppServerTest {
pub termination_handler: Option<std::sync::mpsc::Receiver<()>>,
}
/// This represents a some source of IO operations in the context of the Rosenpass server
///
/// I.e. this identifies some structure that could be marked as "ready for IO" by [mio]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum AppServerIoSource {
/// IO source refers to a socket in [AppServer::sockets]
Socket(usize),
/// IO source refers to a PSK broker in [AppServer::brokers]
PskBroker(Public<BROKER_ID_BYTES>),
/// IO source refers to some IO sources used in the API;
/// see [AppServer::api_manager]
#[cfg(feature = "experiment_api")]
MioManager(crate::api::mio::MioManagerIoSource),
}
/// Number of epoll(7) events Rosenpass can receive at a time
const EVENT_CAPACITY: usize = 20;
/// Holds the state of the application, namely the external IO
/// This holds pretty much all of the state of the Rosenpass application
/// including the cryptographic state in [Self::crypto_site]
///
/// Responsible for file IO, network IO
// TODO add user control via unix domain socket and stdin/stdout
/// Responsible for file IO, network IO, etc…
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/app_server_example.rs")]
#[doc = "```"]
#[derive(Debug)]
pub struct AppServer {
/// Contains the actual cryptographic implementation.
///
/// Because the API supports initializing the server with a keypair
/// and CryptoServer needs to be initialized with a keypair, the struct
/// is wrapped in a ConstructionSite
pub crypto_site: ConstructionSite<BuildCryptoServer, CryptoServer>,
/// The UDP sockets used to send and receive protocol messages
pub sockets: Vec<mio::net::UdpSocket>,
/// Buffer for [mio] (epoll(7), async IO handling) IO events
pub events: mio::Events,
/// Supplemental buffer for [mio] events. See the inline documentation of [AppServer::try_recv]
/// for details.
pub short_poll_queue: VecDeque<mio::event::Event>,
/// We have two different polling modes; long polling (legacy) and short polling (new and
/// somewhat experimental). See [AppServer::try_recv] for details
pub performed_long_poll: bool,
/// Events produced by [mio] refer to IO sources by a numeric token assigned to them
/// (see [MioTokenDispenser]). This index associateds mio token with the specific IO
/// source stored in this object
pub io_source_index: HashMap<mio::Token, AppServerIoSource>,
/// Asynchronous IO source
pub mio_poll: mio::Poll,
/// MIO associates IO sources with numeric tokens. This struct takes care of generating these
/// tokens
pub mio_token_dispenser: MioTokenDispenser,
/// Helpers handling communication with WireGuard; these take a generated key and forward it to
/// WireGuard
pub brokers: BrokerStore,
/// This is our view of the peers; generally every peer in here is associated with one peer in
/// CryptoServer
pub peers: Vec<AppPeer>,
/// If set to [Verbosity::Verbose], then some extra information will be printed
/// at the info log level
pub verbosity: Verbosity,
/// Used by [AppServer::try_recv] to ensure that all packages have been read
/// from the UDP sockets
pub all_sockets_drained: bool,
/// Whether network message handling determined that a Denial of Service attack is happening
pub under_load: DoSOperation,
/// State kept by the [AppServer::try_recv] for polling
pub blocking_polls_count: usize,
/// State kept by the [AppServer::try_recv] for polling
pub non_blocking_polls_count: usize,
/// State kept by the [AppServer::try_recv] for polling
pub unpolled_count: usize,
/// State kept by the [AppServer::try_recv] for polling
pub last_update_time: Instant,
/// Used by integration tests to force [Self] into DoS condition
/// and to terminate the AppServer after the test is complete
pub test_helpers: Option<AppServerTest>,
/// Helper for integration tests running rosenpass as a subprocess
/// to terminate properly upon receiving an appropriate system signal.
///
/// This is primarily needed for coverage testing, since llvm-cov does not
/// write coverage reports to disk when a process is stopped by the default
/// signal handler.
///
/// See <https://github.com/rosenpass/rosenpass/issues/385>
#[cfg(feature = "internal_signal_handling_for_coverage_reports")]
pub term_signal: terminate::TerminateRequested,
#[cfg(feature = "experiment_api")]
/// The Rosenpass unix socket API handler; this is an experimental
/// feature that can be used to embed Rosenpass in external applications
/// via communication by unix socket
pub api_manager: crate::api::mio::MioManager,
}
@@ -199,14 +406,19 @@ pub struct AppServer {
pub struct SocketPtr(pub usize);
impl SocketPtr {
/// Retrieve the concrete udp socket associated with the pointer
pub fn get<'a>(&self, srv: &'a AppServer) -> &'a mio::net::UdpSocket {
&srv.sockets[self.0]
}
/// Retrieve the concrete udp socket associated with the pointer, mutably
pub fn get_mut<'a>(&self, srv: &'a mut AppServer) -> &'a mut mio::net::UdpSocket {
&mut srv.sockets[self.0]
}
/// Send a UDP packet to another address.
///
/// Merely forwards to [mio::net::UdpSocket::send_to]
pub fn send_to(&self, srv: &AppServer, buf: &[u8], addr: SocketAddr) -> anyhow::Result<()> {
self.get(srv).send_to(buf, addr)?;
Ok(())
@@ -214,28 +426,41 @@ impl SocketPtr {
}
/// Index based pointer to a Peer
///
/// This allows retrieving both the io-oriented and the cryptographic information
/// about a peer.
#[derive(Debug, Copy, Clone)]
pub struct AppPeerPtr(pub usize);
impl AppPeerPtr {
/// Takes an index based handle and returns the actual peer
/// Takes an pointer from the cryptography subsystem
/// in [AppServer::crypto_site] and derives the associated AppPeerPtr
/// in [AppServer]
pub fn lift(p: PeerPtr) -> Self {
Self(p.0)
}
/// Returns an index based handle to one Peer
/// Turns this pointer into a cryptographic peer pointer for [CryptoServer]
/// in [AppServer::crypto_site]
pub fn lower(&self) -> PeerPtr {
PeerPtr(self.0)
}
/// Retrieve the [AppPeer] pointed to by [Self]
pub fn get_app<'a>(&self, srv: &'a AppServer) -> &'a AppPeer {
&srv.peers[self.0]
}
/// Retrieve the [AppPeer] pointed to by [Self], mutably
pub fn get_app_mut<'a>(&self, srv: &'a mut AppServer) -> &'a mut AppPeer {
&mut srv.peers[self.0]
}
/// Use the associated WireGuard PSK broker via [BrokerStorePtr]
/// to upload a new PSK.
///
/// If no PSK broker is set and [AppPeer::outfile] is none, then
/// this prints a warning
pub fn set_psk(&self, server: &mut AppServer, psk: &Secret<WG_KEY_LEN>) -> anyhow::Result<()> {
if let Some(broker) = server.peers[self.0].broker_peer.as_ref() {
let config = broker.peer_cfg.create_config(psk);
@@ -248,17 +473,34 @@ impl AppPeerPtr {
}
}
/// The result of [AppServer::poll].
///
/// Instructs [AppServer::event_loop_without_error_handling] on how to proceed.
#[derive(Debug)]
pub enum AppPollResult {
/// Erase the key for a given peer. Corresponds to [crate::protocol::PollResult::DeleteKey]
DeleteKey(AppPeerPtr),
/// Send an initiation to the given peer. Corresponds to [crate::protocol::PollResult::SendInitiation]
SendInitiation(AppPeerPtr),
/// Send a retransmission to the given peer. Corresponds to
/// [crate::protocol::PollResult::SendRetransmission]
SendRetransmission(AppPeerPtr),
/// Received a network message.
///
/// This is the only case without a correspondence in [crate::protocol::PollResult]
ReceivedMessage(usize, Endpoint),
}
/// The reason why we are outputting a key
#[derive(Debug)]
pub enum KeyOutputReason {
/// The reason is that a new key for the given peer was successfully exchanged
Exchanged,
/// The reason is, that no key could be exchanged with the peer before the output
/// key lifetime was reached; a [AppPollResult::DeleteKey] event was issued.
///
/// The key we output in this case is chosen randomly and serves to securely
/// erase whatever key is currently stored.
Stale,
}
@@ -297,14 +539,21 @@ impl std::fmt::Display for Endpoint {
}
}
/// A network address bound to a particular socket.
///
/// We need the information which socket is used because different listen sockets
/// might be on different networks.
#[derive(Debug)]
pub struct SocketBoundEndpoint {
/// The socket the address can be reached under; this is generally
/// determined when we actually receive an RespHello message
socket: SocketPtr,
/// Just the address
/// The network address
addr: SocketAddr,
/// identifier
/// Byte representation of this socket bound network address.
/// Generated through [SocketBoundEndpoint::to_bytes].
///
/// Read through [HostIdentification::encode]
bytes: (usize, [u8; SocketBoundEndpoint::BUFFER_SIZE]),
}
@@ -315,16 +564,22 @@ impl std::fmt::Display for SocketBoundEndpoint {
}
impl SocketBoundEndpoint {
/// Length in bytes of the serialized socket index
const SOCKET_SIZE: usize = usize::BITS as usize / 8;
/// Length in bytes of the serialized ipv6 address
const IPV6_SIZE: usize = 16;
/// Length in bytes of the serialized port
const PORT_SIZE: usize = 2;
/// Length in bytes of the serialized ipv6 address scope (see [SocketAddrV6::scope_id])
const SCOPE_ID_SIZE: usize = 4;
/// Length in size of
const BUFFER_SIZE: usize = SocketBoundEndpoint::SOCKET_SIZE
+ SocketBoundEndpoint::IPV6_SIZE
+ SocketBoundEndpoint::PORT_SIZE
+ SocketBoundEndpoint::SCOPE_ID_SIZE;
/// Produce a new [Self]
pub fn new(socket: SocketPtr, addr: SocketAddr) -> Self {
let bytes = Self::to_bytes(&socket, &addr);
Self {
@@ -334,6 +589,7 @@ impl SocketBoundEndpoint {
}
}
/// Computes [HostIdentification::encode] for [Self]. Value cached in [Self::bytes].
fn to_bytes(
socket: &SocketPtr,
addr: &SocketAddr,
@@ -368,12 +624,18 @@ impl HostIdentification for SocketBoundEndpoint {
}
impl Endpoint {
/// Start discovery from some addresses
/// Given a list of potential network addresses, start peer discovery.
///
/// Will send initiations to different addresses given here on each [crate::msgs::InitHello]
/// retransmission during the peer discovery phase.
pub fn discovery_from_addresses(addresses: Vec<SocketAddr>) -> Self {
Endpoint::Discovery(HostPathDiscoveryEndpoint::from_addresses(addresses))
}
/// Start endpoint discovery from a hostname
/// Given a hostname, start peer discovery.
///
/// Will send initiations to different addresses assigned to the host name
/// on each [crate::msgs::InitHello] retransmission during the peer discovery phase.
pub fn discovery_from_hostname(hostname: String) -> anyhow::Result<Self> {
let host = HostPathDiscoveryEndpoint::lookup(hostname)?;
Ok(Endpoint::Discovery(host))
@@ -404,6 +666,8 @@ impl Endpoint {
Some(Self::discovery_from_addresses(addrs))
}
/// Send a message to the address referenced by this endpoint or to one of
/// the endpoints if we are in the peer discovery phase for this endpoint
pub fn send(&self, srv: &AppServer, buf: &[u8]) -> anyhow::Result<()> {
use Endpoint::*;
match self {
@@ -412,6 +676,9 @@ impl Endpoint {
}
}
/// List of addresses this endpoint may be associated with.
///
/// During peer discovery, this can be multiple addresses.
fn addresses(&self) -> &[SocketAddr] {
use Endpoint::*;
match self {
@@ -449,7 +716,14 @@ impl Endpoint {
// TODO: We might consider adjusting the retransmission handling to account for host-path discovery
#[derive(Debug)]
pub struct HostPathDiscoveryEndpoint {
scouting_state: Cell<(usize, usize)>, // addr_off, sock_off
/// Round robin index the next [Self::send_scouting] call should send packets to
///
/// (address offset, socket offset)
///
/// Including the socket here accounts for the fact that some network addresses may be
/// reachable only through particular UDP sockets
scouting_state: Cell<(usize, usize)>,
/// List of addresses fir oeer discovery
addresses: Vec<SocketAddr>,
}
@@ -460,6 +734,7 @@ impl std::fmt::Display for HostPathDiscoveryEndpoint {
}
impl HostPathDiscoveryEndpoint {
/// Initiate a peer discovery process through a list of potential addresses
pub fn from_addresses(addresses: Vec<SocketAddr>) -> Self {
let scouting_state = Cell::new((0, 0));
Self {
@@ -468,7 +743,7 @@ impl HostPathDiscoveryEndpoint {
}
}
/// Lookup a hostname
/// Initiate a peer discovery process through hostname lookup
pub fn lookup(hostname: String) -> anyhow::Result<Self> {
Ok(Self {
addresses: ToSocketAddrs::to_socket_addrs(&hostname)?.collect(),
@@ -476,10 +751,14 @@ impl HostPathDiscoveryEndpoint {
})
}
/// List of address candidates for the peer
pub fn addresses(&self) -> &Vec<SocketAddr> {
&self.addresses
}
/// Calculates and stores the next value for [Self::scouting_state]
/// given the address and socket we just sent a scouting [crate::msgs::InitHello] message
/// to
fn insert_next_scout_offset(&self, srv: &AppServer, addr_no: usize, sock_no: usize) {
self.scouting_state.set((
(addr_no + 1) % self.addresses.len(),
@@ -534,6 +813,11 @@ impl HostPathDiscoveryEndpoint {
}
impl AppServer {
/// Construct a new AppServer
///
/// # Examples
///
/// See [Self].
pub fn new(
keypair: Option<(SSk, SPk)>,
addrs: Vec<SocketAddr>,
@@ -633,6 +917,8 @@ impl AppServer {
};
Ok(Self {
#[cfg(feature = "internal_signal_handling_for_coverage_reports")]
term_signal: terminate::TerminateRequested::new()?,
crypto_site,
peers: Vec::new(),
verbosity,
@@ -656,22 +942,37 @@ impl AppServer {
})
}
/// Access the cryptographic protocol server
///
/// This may return an error if [Self] was initialized without a keypair
/// and no keypair has been supplied since then.
///
/// I.e. will return an error if [Self::crypto_site] is not fully initialized
pub fn crypto_server(&self) -> anyhow::Result<&CryptoServer> {
self.crypto_site
.product_ref()
.context("Cryptography handler not initialized")
}
/// Access the cryptographic protocol server, mutably
///
/// This may return an error if [Self] was initialized without a keypair
/// and no keypair has been supplied since then.
///
/// I.e. will return an error if [Self::crypto_site] is not fully initialized
pub fn crypto_server_mut(&mut self) -> anyhow::Result<&mut CryptoServer> {
self.crypto_site
.product_mut()
.context("Cryptography handler not initialized")
}
/// If set to [Verbosity::Verbose], then some extra information will be printed
/// at the info log level
pub fn verbose(&self) -> bool {
matches!(self.verbosity, Verbosity::Verbose)
}
/// Used by [Self::new] to register a new udp listen source
pub fn register_listen_socket(&mut self, mut sock: mio::net::UdpSocket) -> anyhow::Result<()> {
let mio_token = self.mio_token_dispenser.dispense();
self.mio_poll
@@ -683,16 +984,19 @@ impl AppServer {
Ok(())
}
/// Used to register a source of IO such as a listen socket with [Self::io_source_index]
pub fn register_io_source(&mut self, token: mio::Token, io_source: AppServerIoSource) {
let prev = self.io_source_index.insert(token, io_source);
assert!(prev.is_none());
}
/// Unregister an IO source registered with [Self::register_io_source]
pub fn unregister_io_source(&mut self, token: mio::Token) {
let value = self.io_source_index.remove(&token);
assert!(value.is_some(), "Removed IO source that does not exist");
}
/// Register a new WireGuard PSK broker
pub fn register_broker(
&mut self,
broker: Box<dyn WireguardBrokerMio<Error = anyhow::Error, MioError = anyhow::Error>>,
@@ -715,6 +1019,7 @@ impl AppServer {
Ok(BrokerStorePtr(ptr))
}
/// Unregister a WireGuard PSK broker registered with [Self::register_broker]
pub fn unregister_broker(&mut self, ptr: BrokerStorePtr) -> Result<()> {
let mut broker = self
.brokers
@@ -726,6 +1031,11 @@ impl AppServer {
Ok(())
}
/// Register a new protocol peer
///
/// # Examples
///
/// See [Self::new].
pub fn add_peer(
&mut self,
psk: Option<SymKey>,
@@ -733,11 +1043,12 @@ impl AppServer {
outfile: Option<PathBuf>,
broker_peer: Option<BrokerPeer>,
hostname: Option<String>,
protocol_version: ProtocolVersion,
) -> anyhow::Result<AppPeerPtr> {
let PeerPtr(pn) = match &mut self.crypto_site {
ConstructionSite::Void => bail!("Crypto server construction site is void"),
ConstructionSite::Builder(builder) => builder.add_peer(psk, pk),
ConstructionSite::Product(srv) => srv.add_peer(psk, pk)?,
ConstructionSite::Builder(builder) => builder.add_peer(psk, pk, protocol_version),
ConstructionSite::Product(srv) => srv.add_peer(psk, pk, protocol_version.into())?,
};
assert!(pn == self.peers.len());
@@ -754,18 +1065,40 @@ impl AppServer {
Ok(AppPeerPtr(pn))
}
pub fn listen_loop(&mut self) -> anyhow::Result<()> {
/// Main IO handler; this generally does not terminate
///
/// # Examples
///
/// See [Self::new].
pub fn event_loop(&mut self) -> anyhow::Result<()> {
const INIT_SLEEP: f64 = 0.01;
const MAX_FAILURES: i32 = 10;
let mut failure_cnt = 0;
loop {
let msgs_processed = 0usize;
let err = match self.event_loop() {
let err = match self.event_loop_without_error_handling() {
Ok(()) => return Ok(()),
Err(e) => e,
};
#[cfg(feature = "internal_signal_handling_for_coverage_reports")]
{
let terminated_by_signal = err
.downcast_ref::<std::io::Error>()
.filter(|e| e.kind() == std::io::ErrorKind::Interrupted)
.filter(|_| self.term_signal.value())
.is_some();
if terminated_by_signal {
log::warn!(
"\
Terminated by signal; this signal handler is correct during coverage testing \
but should be otherwise disabled"
);
return Ok(());
}
}
// This should not happen…
failure_cnt = if msgs_processed > 0 {
0
@@ -790,7 +1123,10 @@ impl AppServer {
}
}
pub fn event_loop(&mut self) -> anyhow::Result<()> {
/// IO handler without proactive restarts after errors.
///
/// This is used internally in [Self::event_loop].
pub fn event_loop_without_error_handling(&mut self) -> anyhow::Result<()> {
let (mut rx, mut tx) = (MsgBuf::zero(), MsgBuf::zero());
/// if socket address for peer is known, call closure
@@ -909,6 +1245,8 @@ impl AppServer {
}
}
/// Helper for [Self::event_loop_without_error_handling] to handle network messages
/// under DoS condition
fn handle_msg_under_load(
&mut self,
endpoint: &Endpoint,
@@ -925,6 +1263,8 @@ impl AppServer {
}
}
/// Used as a helper by [Self::event_loop_without_error_handling] when
/// a new output key has been exchanged
pub fn output_key(
&mut self,
peer: AppPeerPtr,
@@ -974,6 +1314,10 @@ impl AppServer {
Ok(())
}
/// Poll for events from the cryptographic server ([Self::crypto_server()])
/// and for IO events through [Self::poll].
///
/// Used internally in [Self::event_loop_without_error_handling]
pub fn poll(&mut self, rx_buf: &mut [u8]) -> anyhow::Result<AppPollResult> {
use crate::protocol::PollResult as C;
use AppPollResult as A;
@@ -1005,7 +1349,9 @@ impl AppServer {
Ok(res)
}
/// Tries to receive a new message
/// Tries to receive a new message from the network sockets.
///
/// Used internally in [Self::poll]
///
/// - might wait for an duration up to `timeout`
/// - returns immediately if an error occurs
@@ -1177,6 +1523,7 @@ impl AppServer {
Ok(None)
}
/// Internal helper for [Self::try_recv]
fn perform_mio_poll_and_register_events(&mut self, timeout: Duration) -> io::Result<()> {
self.mio_poll.poll(&mut self.events, Some(timeout))?;
// Fill the short poll buffer with the acquired events
@@ -1187,6 +1534,7 @@ impl AppServer {
Ok(())
}
/// Internal helper for [Self::try_recv]
fn try_recv_from_mio_token(
&mut self,
buf: &mut [u8],
@@ -1203,6 +1551,7 @@ impl AppServer {
self.try_recv_from_io_source(buf, io_source)
}
/// Internal helper for [Self::try_recv]
fn try_recv_from_io_source(
&mut self,
buf: &mut [u8],
@@ -1233,6 +1582,7 @@ impl AppServer {
}
}
/// Internal helper for [Self::try_recv]
fn try_recv_from_listen_socket(
&mut self,
buf: &mut [u8],
@@ -1268,6 +1618,20 @@ impl AppServer {
}
#[cfg(feature = "experiment_api")]
/// This is a wrapper around a reference to [AppServer] that
/// dishes out references to [AppServer::api_manager] and
/// to [AppServer] itself.
///
/// It really just implements [crate::api::mio::MioManagerContext] which
/// provides the methods operating on [crate::api::mio::MioManager] provided
/// through a trait.
///
/// This is a rather complicated way of providing just a few functions. The entire
/// point of this exercise is to decouple the code in the API from [AppServer] and
/// this file a bit, despite those functions all needing access to [AppServer].
///
/// We want the code to live in its own module instead of expanding and expanding the source
/// file with [AppServer] more and more.
struct MioManagerFocus<'a>(&'a mut AppServer);
#[cfg(feature = "experiment_api")]
@@ -1288,3 +1652,48 @@ impl crate::api::mio::MioManagerContext for MioManagerFocus<'_> {
self.0
}
}
/// These signal handlers are used exclusively used during coverage testing
/// to ensure that the llvm-cov can produce reports during integration tests
/// with multiple processes where subprocesses are terminated via kill(2).
///
/// llvm-cov does not support producing coverage reports when the process exits
/// through a signal, so this is necessary.
///
/// The functionality of exiting gracefully upon reception of a terminating signal
/// is desired for the production variant of Rosenpass, but we should make sure
/// to use a higher quality implementation; in particular, we should use signalfd(2).
///
#[cfg(feature = "internal_signal_handling_for_coverage_reports")]
mod terminate {
use signal_hook::flag::register as sig_register;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
/// Automatically register a signal handler for common termination signals;
/// whether one of these signals was issued can be polled using [Self::value].
///
/// The signal handler is not removed when this struct goes out of scope.
#[derive(Debug)]
pub struct TerminateRequested {
value: Arc<AtomicBool>,
}
impl TerminateRequested {
/// Register signal handlers watching for common termination signals
pub fn new() -> anyhow::Result<Self> {
let value = Arc::new(AtomicBool::new(false));
for sig in signal_hook::consts::TERM_SIGNALS.iter().copied() {
sig_register(sig, Arc::clone(&value))?;
}
Ok(Self { value })
}
/// Check whether a termination signal has been set
pub fn value(&self) -> bool {
self.value.load(Ordering::Relaxed)
}
}
}

View File

@@ -1,8 +1,10 @@
use anyhow::{Context, Result};
use heck::ToShoutySnakeCase;
use rosenpass_ciphers::subtle::keyed_hash::KeyedHash;
use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN};
/// Recursively calculate a concrete hash value for an API message type
fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN]> {
match values.split_first() {
Some((head, tail)) => calculate_hash_value(hd.mix(head.as_bytes())?, tail),
@@ -10,12 +12,13 @@ fn calculate_hash_value(hd: HashDomain, values: &[&str]) -> Result<[u8; KEY_LEN]
}
}
fn print_literal(path: &[&str]) -> Result<()> {
let val = calculate_hash_value(HashDomain::zero(), path)?;
/// Print a hash literal for pasting into the Rosenpass source code
fn print_literal(path: &[&str], shake_or_blake: KeyedHash) -> Result<()> {
let val = calculate_hash_value(HashDomain::zero(shake_or_blake.clone()), path)?;
let (last, prefix) = path.split_last().context("developer error!")?;
let var_name = last.to_shouty_snake_case();
print!("// hash domain hash of: ");
print!("// hash domain hash with hash {} of: ", shake_or_blake);
for n in prefix.iter() {
print!("{n} -> ");
}
@@ -33,6 +36,8 @@ fn print_literal(path: &[&str]) -> Result<()> {
Ok(())
}
/// Tree of domain separators where each leaf represents
/// an API message ID
#[derive(Debug, Clone)]
enum Tree {
Branch(String, Vec<Tree>),
@@ -47,27 +52,28 @@ impl Tree {
}
}
fn gen_code_inner(&self, prefix: &[&str]) -> Result<()> {
fn gen_code_inner(&self, prefix: &[&str], shake_or_blake: KeyedHash) -> Result<()> {
let mut path = prefix.to_owned();
path.push(self.name());
match self {
Self::Branch(_, ref children) => {
for c in children.iter() {
c.gen_code_inner(&path)?
c.gen_code_inner(&path, shake_or_blake.clone())?
}
}
Self::Leaf(_) => print_literal(&path)?,
Self::Leaf(_) => print_literal(&path, shake_or_blake)?,
};
Ok(())
}
fn gen_code(&self) -> Result<()> {
self.gen_code_inner(&[])
fn gen_code(&self, shake_or_blake: KeyedHash) -> Result<()> {
self.gen_code_inner(&[], shake_or_blake)
}
}
/// Helper for generating hash-based message IDs for the IPC API
fn main() -> Result<()> {
let tree = Tree::Branch(
"Rosenpass IPC API".to_owned(),
@@ -88,5 +94,7 @@ fn main() -> Result<()> {
println!("type RawMsgType = u128;");
println!();
tree.gen_code()
tree.gen_code(KeyedHash::keyed_shake256())?;
println!();
tree.gen_code(KeyedHash::incorrect_hmac_blake2b())
}

View File

@@ -1,7 +1,12 @@
//! Contains the code used to parse command line parameters for rosenpass.
//!
//! [CliArgs::run] is called by the rosenpass main function and contains the
//! bulk of our boostrapping code while the main function just sets up the basic environment
use anyhow::{bail, ensure, Context};
use clap::{Parser, Subcommand};
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem;
use rosenpass_cipher_traits::primitives::Kem;
use rosenpass_ciphers::StaticKem;
use rosenpass_secret_memory::file::StoreSecret;
use rosenpass_util::file::{LoadValue, LoadValueB64, StoreValue};
use rosenpass_wireguard_broker::brokers::native_unix::{
@@ -31,15 +36,25 @@ use {
std::thread,
};
/// enum representing a choice of interface to a WireGuard broker
/// How to reach a WireGuard PSK Broker
#[derive(Debug)]
pub enum BrokerInterface {
/// The PSK Broker is listening on a unix socket at the given path
Socket(PathBuf),
/// The PSK Broker broker is already connected to this process; a
/// unix socket stream can be reached at the given file descriptor.
///
/// This is generally used with file descriptor passing.
FileDescriptor(i32),
/// Create a socketpair(3p), spawn the PSK broker process from within rosenpass,
/// and hand one end of the socketpair to the broker process via file
/// descriptor passing to the subprocess
SocketPair,
}
/// struct holding all CLI arguments for `clap` crate to parse
/// Command line arguments to the Rosenpass binary.
///
/// Used for parsing with [clap].
#[derive(Parser, Debug)]
#[command(author, version, about, long_about, arg_required_else_help = true)]
pub struct CliArgs {
@@ -80,6 +95,7 @@ pub struct CliArgs {
#[arg(short, long, group = "psk-broker-specs")]
psk_broker_spawn: bool,
/// The subcommand to be invoked
#[command(subcommand)]
pub command: Option<CliCommand>,
@@ -98,6 +114,10 @@ pub struct CliArgs {
}
impl CliArgs {
/// Apply the command line parameters to the Rosenpass configuration struct
///
/// Generally the flow of control here is that all the command line parameters
/// are merged into the configuration file to avoid much code duplication.
pub fn apply_to_config(&self, _cfg: &mut config::Rosenpass) -> anyhow::Result<()> {
#[cfg(feature = "experiment_api")]
self.api.apply_to_config(_cfg)?;
@@ -123,9 +143,11 @@ impl CliArgs {
None
}
/// Return the WireGuard PSK broker interface configured.
///
/// Returns `None` if the `experiment_api` feature is disabled.
#[cfg(feature = "experiment_api")]
/// returns the broker interface set by CLI args
/// returns `None` if the `experiment_api` feature isn't enabled
pub fn get_broker_interface(&self) -> Option<BrokerInterface> {
if let Some(path_ref) = self.psk_broker_path.as_ref() {
Some(BrokerInterface::Socket(path_ref.to_path_buf()))
@@ -138,9 +160,10 @@ impl CliArgs {
}
}
/// Return the WireGuard PSK broker interface configured.
///
/// Returns `None` if the `experiment_api` feature is disabled.
#[cfg(not(feature = "experiment_api"))]
/// returns the broker interface set by CLI args
/// returns `None` if the `experiment_api` feature isn't enabled
pub fn get_broker_interface(&self) -> Option<BrokerInterface> {
None
}
@@ -244,15 +267,17 @@ pub enum CliCommand {
}
impl CliArgs {
/// Runs the command specified via CLI
/// Run Rosenpass with the given command line parameters
///
/// ## TODO
/// - This method consumes the [`CliCommand`] value. It might be wise to use a reference...
/// This contains the bulk of our startup logic with
/// the main function just setting up the basic environment
/// and then calling this function.
pub fn run(
self,
broker_interface: Option<BrokerInterface>,
test_helpers: Option<AppServerTest>,
) -> anyhow::Result<()> {
// TODO: This method consumes the [`CliCommand`] value. It might be wise to use a reference...
use CliCommand::*;
match &self.command {
Some(GenConfig { config_file, force }) => {
@@ -403,6 +428,7 @@ impl CliArgs {
Ok(())
}
/// Used by [Self::run] to start the Rosenpass key exchange server
fn event_loop(
config: config::Rosenpass,
broker_interface: Option<BrokerInterface>,
@@ -464,12 +490,26 @@ impl CliArgs {
cfg_peer.key_out,
broker_peer,
cfg_peer.endpoint.clone(),
cfg_peer.protocol_version.into(),
)?;
}
srv.event_loop()
}
/// Create the WireGuard PSK broker to be used by
/// [crate::app_server::AppServer].
///
/// If the `experiment_api`
/// feature flag is set, then this communicates with a PSK broker
/// running in a different process as configured via
/// the `psk_broker_path`, `psk_broker_fd`, and `psk_broker_spawn`
/// fields.
///
/// If the `experiment_api`
/// feature flag is not set, then this returns a [NativeUnixBroker],
/// sending pre-shared keys directly to WireGuard from within this
/// process.
#[cfg(feature = "experiment_api")]
fn create_broker(
broker_interface: Option<BrokerInterface>,
@@ -485,6 +525,19 @@ impl CliArgs {
}
}
/// Create the WireGuard PSK broker to be used by
/// [crate::app_server::AppServer].
///
/// If the `experiment_api`
/// feature flag is set, then this communicates with a PSK broker
/// running in a different process as configured via
/// the `psk_broker_path`, `psk_broker_fd`, and `psk_broker_spawn`
/// fields.
///
/// If the `experiment_api`
/// feature flag is not set, then this returns a [NativeUnixBroker],
/// sending pre-shared keys directly to WireGuard from within this
/// process.
#[cfg(not(feature = "experiment_api"))]
fn create_broker(
_broker_interface: Option<BrokerInterface>,
@@ -492,6 +545,10 @@ impl CliArgs {
Ok(Box::new(NativeUnixBroker::new()))
}
/// Used by [Self::create_broker] if the `experiment_api` is configured
/// to set up the connection with the PSK broker process as configured
/// via the `psk_broker_path`, `psk_broker_fd`, and `psk_broker_spawn`
/// fields.
#[cfg(feature = "experiment_api")]
fn get_broker_socket(broker_interface: BrokerInterface) -> Result<UnixStream, anyhow::Error> {
// Connect to the psk broker unix socket if one was specified
@@ -549,10 +606,10 @@ impl CliArgs {
}
/// 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<()> {
pub 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.deref_mut())?;
StaticKem.keygen(ssk.secret_mut(), spk.deref_mut())?;
ssk.store_secret(secret_key)?;
spk.store(public_key)
}

View File

@@ -4,8 +4,9 @@
//! [`Rosenpass`] which holds such a configuration.
//!
//! ## TODO
//! - support `~` in <https://github.com/rosenpass/rosenpass/issues/237>
//! - provide tooling to create config file from shell <https://github.com/rosenpass/rosenpass/issues/247>
//! - TODO: support `~` in <https://github.com/rosenpass/rosenpass/issues/237>
//! - TODO: provide tooling to create config file from shell <https://github.com/rosenpass/rosenpass/issues/247>
use crate::protocol::{SPk, SSk};
use rosenpass_util::file::LoadValue;
use std::{
@@ -31,7 +32,10 @@ fn empty_api_config() -> crate::api::config::ApiConfig {
}
}
#[derive(Debug, Serialize, Deserialize)]
/// Configuration for the Rosenpass key exchange
///
/// i.e. configuration for the `rosenpass exchange` and `rosenpass exchange-config` commands
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Rosenpass {
// TODO: Raise error if secret key or public key alone is set during deserialization
// SEE: https://github.com/serde-rs/serde/issues/2793
@@ -46,7 +50,10 @@ pub struct Rosenpass {
/// list of [`SocketAddr`] to listen on
///
/// Examples:
/// - `0.0.0.0:123`
///
/// - `0.0.0.0:123` Listen on any interface using IPv4, port 123
/// - `[::1]:1234` Listen on IPv6 localhost, port 1234
/// - `[::]:4476` Listen on any IPv4 or IPv6 interface, port 4476
pub listen: Vec<SocketAddr>,
/// log verbosity
@@ -68,6 +75,7 @@ pub struct Rosenpass {
pub config_file_path: PathBuf,
}
/// Public key and secret key locations.
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)]
pub struct Keypair {
/// path to the public key file
@@ -78,6 +86,7 @@ pub struct Keypair {
}
impl Keypair {
/// Construct a keypair from its fields
pub fn new<Pk: AsRef<Path>, Sk: AsRef<Path>>(public_key: Pk, secret_key: Sk) -> Self {
let public_key = public_key.as_ref().to_path_buf();
let secret_key = secret_key.as_ref().to_path_buf();
@@ -88,62 +97,84 @@ impl Keypair {
}
}
/// ## TODO
/// - replace this type with [`log::LevelFilter`], also see <https://github.com/rosenpass/rosenpass/pull/246>
/// Level of verbosity for [crate::app_server::AppServer]
///
/// The value of the field [crate::app_server::AppServer::verbosity]. See the field documentation
/// for details.
///
/// - TODO: replace this type with [`log::LevelFilter`], also see <https://github.com/rosenpass/rosenpass/pull/246>
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone)]
pub enum Verbosity {
Quiet,
Verbose,
}
/// ## TODO
/// - examples
/// - documentation
/// The protocol version to be used by a peer.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Copy, Clone, Default)]
pub enum ProtocolVersion {
#[default]
V02,
V03,
}
/// Configuration data for a single Rosenpass peer
#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct RosenpassPeer {
/// path to the public key of the peer
pub public_key: PathBuf,
/// ## TODO
/// - documentation
/// The hostname and port to connect to
///
/// Can be a
///
/// - hostname and port, e.g. `localhost:8876` or `rosenpass.eu:1427`
/// - IPv4 address and port, e.g. `1.2.3.4:7764`
/// - IPv6 address and port, e.g. `[fe80::24]:7890`
pub endpoint: Option<String>,
/// path to the pre-shared key with the peer
/// path to the pre-shared key shared with the peer
///
/// NOTE: this item can be skipped in the config if you do not use a pre-shared key with the peer
pub pre_shared_key: Option<PathBuf>,
/// ## TODO
/// - documentation
/// If this field is set to a path, the Rosenpass will write the exchanged symmetric keys
/// to the given file and write a notification to standard out to let the calling application
/// know that a new key was exchanged
#[serde(default)]
pub key_out: Option<PathBuf>,
/// ## TODO
/// - documentation
/// - make this field only available on binary builds, not on library builds <https://github.com/rosenpass/rosenpass/issues/249>
/// Information for supplying exchanged keys directly to WireGuard
#[serde(flatten)]
pub wg: Option<WireGuard>,
#[serde(default)]
/// The protocol version to use for the exchange
pub protocol_version: ProtocolVersion,
}
/// ## TODO
/// - documentation
/// Information for supplying exchanged keys directly to WireGuard
#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct WireGuard {
/// ## TODO
/// - documentation
/// Name of the WireGuard interface to supply with pre-shared keys generated by the Rosenpass
/// key exchange
pub device: String,
/// ## TODO
/// - documentation
/// WireGuard public key of the peer to supply with pre-shared keys
pub peer: String,
/// ## TODO
/// - documentation
/// Extra parameters passed to the `wg` command
#[serde(default)]
pub extra_params: Vec<String>,
}
impl Default for Rosenpass {
/// Generate an empty configuration
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_new.rs")]
#[doc = "```"]
fn default() -> Self {
Self::empty()
}
@@ -156,8 +187,15 @@ impl Rosenpass {
/// checked whether they even exist.
///
/// ## TODO
///
/// - consider using a different algorithm to determine home directory the below one may
/// behave unexpectedly on Windows
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_store.rs")]
#[doc = "```"]
pub fn load<P: AsRef<Path>>(p: P) -> anyhow::Result<Self> {
// read file and deserialize
let mut config: Self = toml::from_str(&fs::read_to_string(&p)?)?;
@@ -185,7 +223,13 @@ impl Rosenpass {
Ok(config)
}
/// Write a config to a file
/// Encode a configuration object as toml and write it to a file
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_store.rs")]
#[doc = "```"]
pub fn store<P: AsRef<Path>>(&self, p: P) -> anyhow::Result<()> {
let serialized_config =
toml::to_string_pretty(&self).expect("unable to serialize the default config");
@@ -194,6 +238,12 @@ impl Rosenpass {
}
/// Commit the configuration to where it came from, overwriting the original file
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_store.rs")]
#[doc = "```"]
pub fn commit(&self) -> anyhow::Result<()> {
let mut f = fopen_w(&self.config_file_path, Visibility::Public)?;
f.write_all(toml::to_string_pretty(&self)?.as_bytes())?;
@@ -201,13 +251,21 @@ impl Rosenpass {
self.store(&self.config_file_path)
}
/// Apply the configuration in this object to the given [crate::app_server::AppServer]
pub fn apply_to_app_server(&self, _srv: &mut AppServer) -> anyhow::Result<()> {
#[cfg(feature = "experiment_api")]
self.api.apply_to_app_server(_srv)?;
Ok(())
}
/// Validate a configuration
/// Check that the configuration is sound, ensuring
/// for instance that the referenced files exist
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_validate.rs")]
#[doc = "```"]
pub fn validate(&self) -> anyhow::Result<()> {
if let Some(ref keypair) = self.keypair {
// check the public key file exists
@@ -284,6 +342,21 @@ impl Rosenpass {
Ok(())
}
/// Check that the configuration is useful given the feature set Rosenpass was compiled with
/// and the configuration values.
///
/// This was introduced when we introduced a unix-socket API feature allowing the server
/// keypair to be supplied via the API; in this process we also made [Self::keypair] optional.
/// With respect to this particular feature, this function ensures that [Self::keypair] is set
/// when Rosenpass is compiles without the `experiment_api` flag. When `experiment_api` is
/// used, the function ensures that [Self::keypair] is only `None`, if the Rosenpass API is
/// enabled in the configuration.
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_validate.rs")]
#[doc = "```"]
pub fn check_usefullness(&self) -> anyhow::Result<()> {
#[cfg(not(feature = "experiment_api"))]
ensure!(self.keypair.is_some(), "Server keypair missing.");
@@ -299,15 +372,38 @@ impl Rosenpass {
Ok(())
}
/// Produce an empty confuguration
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_new.rs")]
#[doc = "```"]
pub fn empty() -> Self {
Self::new(None)
}
/// Produce configuration from the keypair
///
/// Shorthand for calling [Self::new] with Some([Keypair]::new(sk, pk)).
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_new.rs")]
#[doc = "```"]
pub fn from_sk_pk<Sk: AsRef<Path>, Pk: AsRef<Path>>(sk: Sk, pk: Pk) -> Self {
Self::new(Some(Keypair::new(pk, sk)))
}
/// Creates a new configuration
/// Initialize a minimal configuration with the [Self::keypair] field supplied
/// as a parameter
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_new.rs")]
#[doc = "```"]
pub fn new(keypair: Option<Keypair>) -> Self {
Self {
keypair,
@@ -321,6 +417,14 @@ impl Rosenpass {
}
/// Add IPv4 __and__ IPv6 IF_ANY address to the listen interfaces
///
/// I.e. listen on any interface.
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_add_if_any.rs")]
#[doc = "```"]
pub fn add_if_any(&mut self, port: u16) {
let ipv4_any = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port));
let ipv6_any = SocketAddr::V6(SocketAddrV6::new(
@@ -333,8 +437,20 @@ impl Rosenpass {
self.listen.push(ipv6_any);
}
/// from chaotic args
/// Quest: the grammar is undecideable, what do we do here?
/// Parser for the old, IP style grammar.
///
/// See out manual page rosenpass-exchange(1) on details about the grammar.
///
/// This function parses the grammar and turns it into an instance of the configuration
/// struct.
///
/// TODO: the grammar is undecidable, what do we do here?
///
/// # Examples
///
#[doc = "```ignore"]
#[doc = include_str!("../tests/config_Rosenpass_parse_args_simple.rs")]
#[doc = "```"]
pub fn parse_args(args: Vec<String>) -> anyhow::Result<Self> {
let mut config = Self::new(Some(Keypair::new("", "")));
@@ -525,11 +641,13 @@ impl Rosenpass {
}
impl Default for Verbosity {
/// Self::Quiet
fn default() -> Self {
Self::Quiet
}
}
/// Example configuration generated by the command `rosenpass gen-config <TOML-FILE>`.
pub static EXAMPLE_CONFIG: &str = r###"public_key = "/path/to/rp-public-key"
secret_key = "/path/to/rp-secret-key"
listen = []
@@ -553,7 +671,7 @@ key_out = "/path/to/rp-key-out.txt" # path to store the key
mod test {
use super::*;
use std::{borrow::Borrow, net::IpAddr};
use std::borrow::Borrow;
fn toml_des<S: Borrow<str>>(s: S) -> Result<toml::Table, toml::de::Error> {
toml::from_str(s.borrow())
@@ -665,34 +783,50 @@ mod test {
}
#[test]
fn test_simple_cli_parse() {
let args = split_str(
"public-key /my/public-key secret-key /my/secret-key verbose \
listen 0.0.0.0:9999 peer public-key /peer/public-key endpoint \
peer.test:9999 outfile /peer/rp-out",
);
fn test_protocol_version() {
let mut rosenpass = Rosenpass::empty();
let mut peer_v_02 = RosenpassPeer::default();
peer_v_02.protocol_version = ProtocolVersion::V02;
rosenpass.peers.push(peer_v_02);
let mut peer_v_03 = RosenpassPeer::default();
peer_v_03.protocol_version = ProtocolVersion::V03;
rosenpass.peers.push(peer_v_03);
#[cfg(feature = "experiment_api")]
{
rosenpass.api.listen_fd = vec![];
rosenpass.api.listen_path = vec![];
rosenpass.api.stream_fd = vec![];
}
#[cfg(feature = "experiment_api")]
let expected_toml = r#"listen = []
verbosity = "Quiet"
[api]
listen_fd = []
listen_path = []
stream_fd = []
let config = Rosenpass::parse_args(args).unwrap();
[[peers]]
protocol_version = "V02"
public_key = ""
assert_eq!(
config.keypair,
Some(Keypair::new("/my/public-key", "/my/secret-key"))
);
assert_eq!(config.verbosity, Verbosity::Verbose);
assert_eq!(
&config.listen,
&vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 9999)]
);
assert_eq!(
config.peers,
vec![RosenpassPeer {
public_key: PathBuf::from("/peer/public-key"),
endpoint: Some("peer.test:9999".into()),
pre_shared_key: None,
key_out: Some(PathBuf::from("/peer/rp-out")),
..Default::default()
}]
)
[[peers]]
protocol_version = "V03"
public_key = ""
"#;
#[cfg(not(feature = "experiment_api"))]
let expected_toml = r#"listen = []
verbosity = "Quiet"
[[peers]]
protocol_version = "V02"
public_key = ""
[[peers]]
protocol_version = "V03"
public_key = ""
"#;
assert_toml_round(rosenpass, expected_toml).unwrap()
}
#[test]

View File

@@ -1,48 +1,328 @@
//! Pseudo Random Functions (PRFs) with a tree-like label scheme which
//! ensures their uniqueness
//! ensures their uniqueness.
//!
//! This ensures [domain separation](https://en.wikipedia.org/wiki/Domain_separation) is used
//! across the Rosenpass protocol.
//!
//! There is a chart containing all hash domains used in Rosenpass in the
//! [whitepaper](https://rosenpass.eu/whitepaper.pdf) ([/papers/whitepaper.md] in this repository).
//!
//! # Tutorial
//!
//! ```
//! use rosenpass::{hash_domain, hash_domain_ns};
//! use rosenpass::hash_domains::protocol;
//!
//! use rosenpass_ciphers::subtle::keyed_hash::KeyedHash;
//!
//! // Declaring a custom hash domain
//! hash_domain_ns!(protocol, custom_domain, "my custom hash domain label");
//!
//! // Declaring a custom hashers
//! hash_domain_ns!(custom_domain, hashers, "hashers");
//! hash_domain_ns!(hashers, hasher1, "1");
//! hash_domain_ns!(hashers, hasher2, "2");
//!
//! // Declaring specific domain separators
//! hash_domain_ns!(custom_domain, domain_separators, "domain separators");
//! hash_domain!(domain_separators, sep1, "1");
//! hash_domain!(domain_separators, sep2, "2");
//!
//! // We use the SHAKE256 hash function for this example
//! let hash_choice = KeyedHash::keyed_shake256();
//!
//! // Generating values under hasher1 with both domain separators
//! let h1 = hasher1(hash_choice.clone())?.mix(b"some data")?.dup();
//! let h1v1 = h1.mix(&sep1(hash_choice.clone())?)?.mix(b"More data")?.into_value();
//! let h1v2 = h1.mix(&sep2(hash_choice.clone())?)?.mix(b"More data")?.into_value();
//!
//! // Generating values under hasher2 with both domain separators
//! let h2 = hasher2(hash_choice.clone())?.mix(b"some data")?.dup();
//! let h2v1 = h2.mix(&sep1(hash_choice.clone())?)?.mix(b"More data")?.into_value();
//! let h2v2 = h2.mix(&sep2(hash_choice.clone())?)?.mix(b"More data")?.into_value();
//!
//! // All of the domain separators are now different, random strings
//! let values = [h1v1, h1v2, h2v1, h2v2];
//! for i in 0..values.len() {
//! for j in (i+1)..values.len() {
//! assert_ne!(values[i], values[j]);
//! }
//! }
//!
//! Ok::<(), anyhow::Error>(())
//! ```
use anyhow::Result;
use rosenpass_ciphers::{hash_domain::HashDomain, KEY_LEN};
use rosenpass_ciphers::hash_domain::HashDomain;
use rosenpass_ciphers::subtle::keyed_hash::KeyedHash;
/// Declare a hash function
///
/// # Examples
///
/// See the source file for details about how this is used concretely.
///
/// See the [module](self) documentation on how to use the hash domains in general
// TODO Use labels that can serve as identifiers
#[macro_export]
macro_rules! hash_domain_ns {
($base:ident, $name:ident, $($lbl:expr),* ) => {
pub fn $name() -> Result<HashDomain> {
let t = $base()?;
($(#[$($attrss:tt)*])* $base:ident, $name:ident, $($lbl:expr),+ ) => {
$(#[$($attrss)*])*
pub fn $name(hash_choice: KeyedHash) -> ::anyhow::Result<::rosenpass_ciphers::hash_domain::HashDomain> {
let t = $base(hash_choice)?;
$( let t = t.mix($lbl.as_bytes())?; )*
Ok(t)
}
}
}
/// Declare a concrete hash value
///
/// # Examples
///
/// See the source file for details about how this is used concretely.
///
/// See the [module](self) documentation on how to use the hash domains in general
#[macro_export]
macro_rules! hash_domain {
($base:ident, $name:ident, $($lbl:expr),* ) => {
pub fn $name() -> Result<[u8; KEY_LEN]> {
let t = $base()?;
($(#[$($attrss:tt)*])* $base:ident, $name:ident, $($lbl:expr),+ ) => {
$(#[$($attrss)*])*
pub fn $name(hash_choice: KeyedHash) -> ::anyhow::Result<[u8; ::rosenpass_ciphers::KEY_LEN]> {
let t = $base(hash_choice)?;
$( 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())
/// The hash domain containing the protocol string.
///
/// This serves as a global [domain separator](https://en.wikipedia.org/wiki/Domain_separation)
/// used in various places in the rosenpass protocol.
///
/// This is generally used to create further hash-domains for specific purposes. Depending on
/// the used hash function, the protocol string is different.
///
/// # Examples
///
/// See the source file for details about how this is used concretely.
///
/// See the [module](self) documentation on how to use the hash domains in general
pub fn protocol(hash_choice: KeyedHash) -> Result<HashDomain> {
// Depending on the hash function, we use different protocol strings
match hash_choice {
KeyedHash::KeyedShake256(_) => HashDomain::zero(hash_choice)
.mix("Rosenpass v1 mceliece460896 Kyber512 ChaChaPoly1305 SHAKE256".as_bytes()),
KeyedHash::IncorrectHmacBlake2b(_) => HashDomain::zero(hash_choice)
.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, cookie_value, "cookie-value");
hash_domain_ns!(protocol, cookie_key, "cookie-key");
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_ns!(
/// Hash domain based on [protocol] for calculating [crate::msgs::Envelope::mac].
///
/// # Examples
///
/// See the source of [crate::msgs::Envelope::seal] and [crate::msgs::Envelope::check_seal]
/// to figure out how this is concretely used.
///
/// See the [module](self) documentation on how to use the hash domains in general.
protocol, mac, "mac");
hash_domain_ns!(
/// Hash domain based on [protocol] involved in calculating [crate::msgs::Envelope::cookie].
///
/// # Examples
///
/// See the source of [crate::msgs::Envelope::seal_cookie],
/// [crate::protocol::CryptoServer::handle_msg_under_load], and
/// [crate::protocol::CryptoServer::handle_cookie_reply]
/// to figure out how this is concretely used.
///
/// See the [module](self) documentation on how to use the hash domains in general.
protocol, cookie, "cookie");
hash_domain_ns!(
/// Hash domain based on [protocol] involved in calculating [crate::msgs::Envelope::cookie].
///
/// # Examples
///
/// See the source of [crate::msgs::Envelope::seal_cookie],
/// [crate::protocol::CryptoServer::handle_msg_under_load], and
/// [crate::protocol::CryptoServer::handle_cookie_reply]
/// to figure out how this is concretely used.
///
/// See the [module](self) documentation on how to use the hash domains in general.
protocol, cookie_value, "cookie-value");
hash_domain_ns!(
/// Hash domain based on [protocol] involved in calculating [crate::msgs::Envelope::cookie].
///
/// # Examples
///
/// See the source of [crate::msgs::Envelope::seal_cookie],
/// [crate::protocol::CryptoServer::handle_msg_under_load], and
/// [crate::protocol::CryptoServer::handle_cookie_reply]
/// to figure out how this is concretely used.
///
/// See the [module](self) documentation on how to use the hash domains in general.
protocol, cookie_key, "cookie-key");
hash_domain_ns!(
/// Hash domain based on [protocol] for calculating the peer id as transmitted (encrypted)
/// in [crate::msgs::InitHello::pidic].
///
/// # Examples
///
/// See the source of [crate::protocol::CryptoServer::pidm] and
/// [crate::protocol::Peer::pidt]
/// to figure out how this is concretely used.
///
/// See the [module](self) documentation on how to use the hash domains in general.
protocol, peerid, "peer id");
hash_domain_ns!(
/// Hash domain based on [protocol] for calculating the additional data
/// during [crate::msgs::Biscuit] encryption, storing the biscuit into
/// [crate::msgs::RespHello::biscuit].
///
/// # Examples
///
/// To understand how the biscuit is used, it is best to read
/// the code of [crate::protocol::HandshakeState::store_biscuit] and
/// [crate::protocol::HandshakeState::load_biscuit]
///
/// See the [module](self) documentation on how to use the hash domains in general.
protocol, biscuit_ad, "biscuit additional data");
hash_domain_ns!(
/// This hash domain begins our actual handshake procedure, initializing the
/// chaining key [crate::protocol::HandshakeState::ck].
///
/// # Examples
///
/// To understand how the chaining key is used, study
/// [crate::protocol::HandshakeState], especially [crate::protocol::HandshakeState::init]
/// and [crate::protocol::HandshakeState::mix].
///
/// See the [module](self) documentation on how to use the hash domains in general.
protocol, ckinit, "chaining key init");
hash_domain_ns!(
/// Namespace for chaining key usage domain separators.
///
/// During the execution of the Rosenpass protocol, we use the chaining key for multiple
/// purposes, so to make sure that we have unique value domains, we mix a domain separator
/// into the chaining key before using it for any particular purpose.
///
/// We could use the full domain separation strings, but using a hash value here is nice
/// because it does not lead to any constraints about domain separator format and we can
/// even allow third parties to define their own separators by claiming a namespace.
///
/// # Examples
///
/// To understand how the chaining key is used, study
/// [crate::protocol::HandshakeState], especially [crate::protocol::HandshakeState::init]
/// and [crate::protocol::HandshakeState::mix].
///
/// See the [module](self) documentation on how to use the hash domains in general.
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!(
/// Used to mix in further values into the chaining key during the handshake.
///
/// See [_ckextract].
///
/// # Examples
///
/// To understand how the chaining key is used, study
/// [crate::protocol::HandshakeState], especially [crate::protocol::HandshakeState::init]
/// and [crate::protocol::HandshakeState::mix].
///
/// See the [module](self) documentation on how to use the hash domains in general.
_ckextract, mix, "mix");
hash_domain!(
/// Chaining key domain separator for generating encryption keys that can
/// encrypt parts of the handshake.
///
/// See [_ckextract].
///
/// # Examples
///
/// Encryption of data during the handshake happens in
/// [crate::protocol::HandshakeState::encrypt_and_mix] and decryption happens in
/// [crate::protocol::HandshakeState::decrypt_and_mix]. See their source code
/// for details.
///
/// To understand how the chaining key is used, study
/// [crate::protocol::HandshakeState], especially [crate::protocol::HandshakeState::init]
/// and [crate::protocol::HandshakeState::mix].
///
/// See the [module](self) documentation on how to use the hash domains in general.
_ckextract, hs_enc, "handshake encryption");
hash_domain!(
/// Chaining key domain separator for live data encryption.
/// Live data encryption is only used to send confirmation of handshake
/// done in [crate::msgs::EmptyData].
///
/// See [_ckextract].
///
/// # Examples
///
/// This domain separator finds use in [crate::protocol::HandshakeState::enter_live].
///
/// To understand how the chaining key is used, study
/// [crate::protocol::HandshakeState], especially [crate::protocol::HandshakeState::init]
/// and [crate::protocol::HandshakeState::mix].
///
/// See the [module](self) documentation on how to use the hash domains in general.
_ckextract, ini_enc, "initiator handshake encryption");
hash_domain!(
/// Chaining key domain separator for live data encryption.
/// Live data encryption is only used to send confirmation of handshake
/// done in [crate::msgs::EmptyData].
///
/// See [_ckextract].
///
/// # Examples
///
/// This domain separator finds use in [crate::protocol::HandshakeState::enter_live].
/// Check out its source code!
///
/// To understand how the chaining key is used, study
/// [crate::protocol::HandshakeState], especially [crate::protocol::HandshakeState::init]
/// and [crate::protocol::HandshakeState::mix].
///
/// See the [module](self) documentation on how to use the hash domains in general.
_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");
hash_domain_ns!(
/// Chaining key domain separator for any usage specific purposes.
///
/// We do recommend that third parties base their specific domain separators
/// on a internet domain and/or mix in much more specific information.
///
/// We only really use this to derive a output key for wireguard; see [osk].
///
/// See [_ckextract].
///
/// # Examples
///
/// See the [module](self) documentation on how to use the hash domains in general.
_ckextract, _user, "user");
hash_domain_ns!(
/// Chaining key domain separator for any rosenpass specific purposes.
///
/// We only really use this to derive a output key for wireguard; see [osk].
///
/// See [_ckextract].
///
/// # Examples
///
/// See the [module](self) documentation on how to use the hash domains in general.
_user, _rp, "rosenpass.eu");
hash_domain!(
/// Chaining key domain separator for deriving the key sent to WireGuard.
///
/// See [_ckextract].
///
/// # Examples
///
/// This domain separator finds use in [crate::protocol::CryptoServer::osk].
/// Check out its source code!
///
/// See the [module](self) documentation on how to use the hash domains in general.
_rp, osk, "wireguard psk");

View File

@@ -1,3 +1,18 @@
//! This is the central rosenpass crate implementing the rosenpass protocol.
//!
//! - [crate::app_server] contains the business logic of rosenpass, handling networking
//! - [crate::cli] contains the cli parsing logic and contains quite a bit of startup logic; the
//! main function quickly hands over to [crate::cli::CliArgs::run] which contains quite a bit
//! of our startup logic
//! - [crate::config] has the code to parse and generate configuration files
//! - [crate::hash_domains] lists the different hash function domains used in the Rosenpass
//! protocol
//! - [crate::msgs] provides declarations of the Rosenpass protocol network messages and facilities
//! to parse those messages through the [::zerocopy] crate
//! - [crate::protocol] this is where the bulk of our code lives; this module contains the actual
//! cryptographic protocol logic
//! - crate::api implements the Rosenpass unix socket API, if feature "experiment_api" is active
#[cfg(feature = "experiment_api")]
pub mod api;
pub mod app_server;
@@ -7,14 +22,25 @@ pub mod hash_domains;
pub mod msgs;
pub mod protocol;
/// Error types used in diverse places across Rosenpass
#[derive(thiserror::Error, Debug)]
pub enum RosenpassError {
/// Usually indicates that parsing a struct through the
/// [::zerocopy] crate failed
#[error("buffer size mismatch")]
BufferSizeMismatch,
/// Mostly raised by the `TryFrom<u8>` implementation for [crate::msgs::MsgType]
/// to indicate that a message type is not defined
#[error("invalid message type")]
InvalidMessageType(u8),
InvalidMessageType(
/// The message type that could not be parsed
u8,
),
/// Raised by the `TryFrom<RawMsgType>` (crate::api::RawMsgType) implementation for crate::api::RequestMsgType
/// and crate::api::RequestMsgType to indicate that a message type is not defined
#[error("invalid API message type")]
InvalidApiMessageType(u128),
#[error("could not parse API message")]
InvalidApiMessage,
InvalidApiMessageType(
/// The message type that could not be parsed
u128,
),
}

View File

@@ -1,3 +1,5 @@
//! For the main function
use clap::CommandFactory;
use clap::Parser;
use clap_mangen::roff::{roman, Roff};
@@ -5,6 +7,7 @@ use log::error;
use rosenpass::cli::CliArgs;
use std::process::exit;
/// Printing custom man sections when generating the man page
fn print_custom_man_section(section: &str, text: &str, file: &mut std::fs::File) {
let mut roff = Roff::default();
roff.control("SH", [section]);
@@ -13,6 +16,8 @@ fn print_custom_man_section(section: &str, text: &str, file: &mut std::fs::File)
}
/// Catches errors, prints them through the logger, then exits
///
/// The bulk of the command line logic is handled inside [crate::cli::CliArgs::run].
pub fn main() {
// parse CLI arguments
let args = CliArgs::parse();
@@ -81,21 +86,27 @@ pub fn main() {
}
}
}
/// Custom main page section: Exit Status
static EXIT_STATUS_MAN: &str = r"
The rosenpass utility exits 0 on success, and >0 if an error occurs.";
/// Custom main page section: See also.
static SEE_ALSO_MAN: &str = r"
rp(1), wg(1)
Karolin Varner, Benjamin Lipp, Wanja Zaeske, and Lisa Schmidt, Rosenpass, https://rosenpass.eu/whitepaper.pdf, 2023.";
/// Custom main page section: Standards.
static STANDARDS_MAN: &str = r"
This tool is the reference implementation of the Rosenpass protocol, as
specified within the whitepaper referenced above.";
/// Custom main page section: Authors.
static AUTHORS_MAN: &str = r"
Rosenpass was created by Karolin Varner, Benjamin Lipp, Wanja Zaeske, Marei
Peischl, Stephan Ajuvo, and Lisa Schmidt.";
/// Custom main page section: Bugs.
static BUGS_MAN: &str = r"
The bugs are tracked at https://github.com/rosenpass/rosenpass/issues.";

View File

@@ -12,17 +12,71 @@ use std::mem::size_of;
use zerocopy::{AsBytes, FromBytes, FromZeroes};
use super::RosenpassError;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::{EphemeralKem, StaticKem};
use rosenpass_ciphers::{aead, xaead, KEY_LEN};
pub const MSG_SIZE_LEN: usize = 1;
pub const RESERVED_LEN: usize = 3;
pub const MAC_SIZE: usize = 16;
pub const COOKIE_SIZE: usize = 16;
pub const SID_LEN: usize = 4;
use rosenpass_cipher_traits::primitives::{Aead as _, Kem};
use rosenpass_ciphers::{Aead, XAead, KEY_LEN};
use rosenpass_ciphers::{EphemeralKem, StaticKem};
/// Length of a session ID such as [InitHello::sidi]
pub const SESSION_ID_LEN: usize = 4;
/// Length of a biscuit ID; i.e. size of the value in [Biscuit::biscuit_no]
pub const BISCUIT_ID_LEN: usize = 12;
/// TODO: Unused, remove!
pub const WIRE_ENVELOPE_LEN: usize = 1 + 3 + 16 + 16; // TODO verify this
/// Size required to fit any message in binary form
pub const MAX_MESSAGE_LEN: usize = 2500; // TODO fix this
/// length in bytes of an unencrypted Biscuit (plain text)
pub const BISCUIT_PT_LEN: usize = size_of::<Biscuit>();
/// Length in bytes of an encrypted Biscuit (cipher text)
pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + XAead::NONCE_LEN + XAead::TAG_LEN;
/// Size of the field [Envelope::mac]
pub const MAC_SIZE: usize = 16;
/// Size of the field [Envelope::cookie]
pub const COOKIE_SIZE: usize = MAC_SIZE;
/// Type of the mac field in [Envelope]
pub type MsgEnvelopeMac = [u8; MAC_SIZE];
/// Type of the cookie field in [Envelope]
pub type MsgEnvelopeCookie = [u8; COOKIE_SIZE];
/// Header and footer included in all our packages,
/// including a type field.
///
/// # Examples
///
/// ```
/// use rosenpass::msgs::{Envelope, InitHello};
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
/// use memoffset::offset_of;
///
/// // Zero-initialization
/// let mut ih = Envelope::<InitHello>::new_zeroed();
///
/// // Edit fields normally
/// ih.mac[0] = 1;
///
/// // Edit as binary
/// ih.as_bytes_mut()[offset_of!(Envelope<InitHello>, msg_type)] = 23;
/// assert_eq!(ih.msg_type, 23);;
///
/// // Conversion to bytes
/// let mut ih2 = ih.as_bytes().to_owned();
///
/// // Setting msg_type field, again
/// ih2[0] = 42;
///
/// // Zerocopy parsing
/// let ih3 = Ref::<&mut [u8], Envelope<InitHello>>::new(&mut ih2).unwrap();
/// assert_ne!(ih.as_bytes(), ih3.as_bytes());
/// assert_eq!(ih3.msg_type, 42);
/// ```
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
#[derive(AsBytes, FromBytes, FromZeroes, Clone)]
pub struct Envelope<M: AsBytes + FromBytes> {
/// [MsgType] of this message
pub msg_type: u8,
@@ -32,11 +86,45 @@ pub struct Envelope<M: AsBytes + FromBytes> {
pub payload: M,
/// Message Authentication Code (mac) over all bytes until (exclusive)
/// `mac` itself
pub mac: [u8; 16],
pub mac: MsgEnvelopeMac,
/// Currently unused, TODO: do something with this
pub cookie: [u8; 16],
pub cookie: MsgEnvelopeCookie,
}
/// This is the first message sent by the initiator to the responder
/// during the execution of the Rosenpass protocol.
///
/// When transmitted on the wire, this type will generally be wrapped into [Envelope].
///
/// # Examples
///
/// Check out the code of [crate::protocol::CryptoServer::handle_initiation] (generation on
/// iniatiator side) and [crate::protocol::CryptoServer::handle_init_hello] (processing on
/// responder side) to understand how this is used.
///
/// [Envelope] contains some extra examples on how to use structures from the [::zerocopy] crate.
///
/// ```
/// use rosenpass::msgs::{Envelope, InitHello};
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
/// use memoffset::span_of;
///
/// // Zero initialization
/// let mut ih = Envelope::<InitHello>::new_zeroed();
///
/// // Conversion to byte representation
/// let ih = ih.as_bytes_mut();
///
/// // Set value on byte representation
/// ih[span_of!(Envelope<InitHello>, payload)][span_of!(InitHello, sidi)]
/// .copy_from_slice(&[1,2,3,4]);
///
/// // Conversion from bytes
/// let ih = Ref::<&mut [u8], Envelope<InitHello>>::new(ih).unwrap();
///
/// // Check that write above on byte representation was effective
/// assert_eq!(ih.payload.sidi, [1,2,3,4]);
/// ```
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
pub struct InitHello {
@@ -47,11 +135,45 @@ pub struct InitHello {
/// Classic McEliece Ciphertext
pub sctr: [u8; StaticKem::CT_LEN],
/// Encryped: 16 byte hash of McEliece initiator static key
pub pidic: [u8; aead::TAG_LEN + 32],
pub pidic: [u8; Aead::TAG_LEN + 32],
/// Encrypted TAI64N Time Stamp (against replay attacks)
pub auth: [u8; aead::TAG_LEN],
pub auth: [u8; Aead::TAG_LEN],
}
/// This is the second message sent by the responder to the initiator
/// during the execution of the Rosenpass protocol in response to [InitHello].
///
/// When transmitted on the wire, this type will generally be wrapped into [Envelope].
///
/// # Examples
///
/// Check out the code of [crate::protocol::CryptoServer::handle_init_hello] (generation on
/// responder side) and [crate::protocol::CryptoServer::handle_resp_hello] (processing on
/// initiator side) to understand how this is used.
///
/// [Envelope] contains some extra examples on how to use structures from the [::zerocopy] crate.
///
/// ```
/// use rosenpass::msgs::{Envelope, RespHello};
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
/// use memoffset::span_of;
///
/// // Zero initialization
/// let mut ih = Envelope::<RespHello>::new_zeroed();
///
/// // Conversion to byte representation
/// let ih = ih.as_bytes_mut();
///
/// // Set value on byte representation
/// ih[span_of!(Envelope<RespHello>, payload)][span_of!(RespHello, sidi)]
/// .copy_from_slice(&[1,2,3,4]);
///
/// // Conversion from bytes
/// let ih = Ref::<&mut [u8], Envelope<RespHello>>::new(ih).unwrap();
///
/// // Check that write above on byte representation was effective
/// assert_eq!(ih.payload.sidi, [1,2,3,4]);
/// ```
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
pub struct RespHello {
@@ -64,13 +186,47 @@ pub struct RespHello {
/// Classic McEliece Ciphertext
pub scti: [u8; StaticKem::CT_LEN],
/// Empty encrypted message (just an auth tag)
pub auth: [u8; aead::TAG_LEN],
pub auth: [u8; Aead::TAG_LEN],
/// Responders handshake state in encrypted form
pub biscuit: [u8; BISCUIT_CT_LEN],
}
/// This is the third message sent by the initiator to the responder
/// during the execution of the Rosenpass protocol in response to [RespHello].
///
/// When transmitted on the wire, this type will generally be wrapped into [Envelope].
///
/// # Examples
///
/// Check out the code of [crate::protocol::CryptoServer::handle_resp_hello] (generation on
/// initiator side) and [crate::protocol::CryptoServer::handle_init_conf] (processing on
/// responder side) to understand how this is used.
///
/// [Envelope] contains some extra examples on how to use structures from the [::zerocopy] crate.
///
/// ```
/// use rosenpass::msgs::{Envelope, InitConf};
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
/// use memoffset::span_of;
///
/// // Zero initialization
/// let mut ih = Envelope::<InitConf>::new_zeroed();
///
/// // Conversion to byte representation
/// let ih = ih.as_bytes_mut();
///
/// // Set value on byte representation
/// ih[span_of!(Envelope<InitConf>, payload)][span_of!(InitConf, sidi)]
/// .copy_from_slice(&[1,2,3,4]);
///
/// // Conversion from bytes
/// let ih = Ref::<&mut [u8], Envelope<InitConf>>::new(ih).unwrap();
///
/// // Check that write above on byte representation was effective
/// assert_eq!(ih.payload.sidi, [1,2,3,4]);
/// ```
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
#[derive(AsBytes, FromBytes, FromZeroes, Debug)]
pub struct InitConf {
/// Copied from InitHello
pub sidi: [u8; 4],
@@ -79,20 +235,81 @@ pub struct InitConf {
/// Responders handshake state in encrypted form
pub biscuit: [u8; BISCUIT_CT_LEN],
/// Empty encrypted message (just an auth tag)
pub auth: [u8; aead::TAG_LEN],
pub auth: [u8; Aead::TAG_LEN],
}
/// This is the fourth message sent by the initiator to the responder
/// during the execution of the Rosenpass protocol in response to [RespHello].
///
/// When transmitted on the wire, this type will generally be wrapped into [Envelope].
///
/// This message does not serve a cryptographic purpose; it just tells the initiator
/// to stop package retransmission.
///
/// This message should really be called `RespConf`, but when we wrote the protocol,
/// we initially designed the protocol we still though Rosenpass itself should do
/// payload transmission at some point so `EmptyData` could have served as a more generic
/// mechanism.
///
/// We might add payload transmission in the future again, but we will treat
/// it as a protocol extension if we do.
///
/// # Examples
///
/// Check out the code of [crate::protocol::CryptoServer::handle_init_conf] (generation on
/// responder side) and [crate::protocol::CryptoServer::handle_resp_conf] (processing on
/// initiator side) to understand how this is used.
///
/// [Envelope] contains some extra examples on how to use structures from the [::zerocopy] crate.
///
/// ```
/// use rosenpass::msgs::{Envelope, EmptyData};
/// use zerocopy::{AsBytes, FromBytes, Ref, FromZeroes};
/// use memoffset::span_of;
///
/// // Zero initialization
/// let mut ih = Envelope::<EmptyData>::new_zeroed();
///
/// // Conversion to byte representation
/// let ih = ih.as_bytes_mut();
///
/// // Set value on byte representation
/// ih[span_of!(Envelope<EmptyData>, payload)][span_of!(EmptyData, sid)]
/// .copy_from_slice(&[1,2,3,4]);
///
/// // Conversion from bytes
/// let ih = Ref::<&mut [u8], Envelope<EmptyData>>::new(ih).unwrap();
///
/// // Check that write above on byte representation was effective
/// assert_eq!(ih.payload.sid, [1,2,3,4]);
/// ```
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
#[derive(AsBytes, FromBytes, FromZeroes, Clone, Copy)]
pub struct EmptyData {
/// Copied from RespHello
pub sid: [u8; 4],
/// Nonce
pub ctr: [u8; 8],
/// Empty encrypted message (just an auth tag)
pub auth: [u8; aead::TAG_LEN],
pub auth: [u8; Aead::TAG_LEN],
}
/// Cookie encrypted and sent to the initiator by the responder in [RespHello]
/// and returned by the initiator in [InitConf].
///
/// The encryption key is randomly chosen by the responder and frequently regenerated.
/// Using this biscuit value in the protocol allows us to make sure that the responder
/// is mostly stateless until full initiator authentication is achieved, which is needed
/// to prevent denial of service attacks. See the [whitepaper](https://rosenpass.eu/whitepaper.pdf)
/// ([/papers/whitepaper.md] in this repository).
///
/// # Examples
///
/// To understand how the biscuit is used, it is best to read
/// the code of [crate::protocol::HandshakeState::store_biscuit] and
/// [crate::protocol::HandshakeState::load_biscuit]
///
/// [Envelope] and [InitHello] contain some extra examples on how to use structures from the [::zerocopy] crate.
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
pub struct Biscuit {
@@ -104,12 +321,20 @@ pub struct Biscuit {
pub ck: [u8; KEY_LEN],
}
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
pub struct DataMsg {
pub dummy: [u8; 4],
}
/// Specialized message for use in the cookie mechanism.
///
/// See the [whitepaper](https://rosenpass.eu/whitepaper.pdf) ([/papers/whitepaper.md] in this repository) for details.
///
/// Generally used together with [CookieReply] which brings this up to the size
/// of [InitHello] to avoid amplification Denial of Service attacks.
///
/// # Examples
///
/// To understand how the biscuit is used, it is best to read
/// the code of [crate::protocol::CryptoServer::handle_cookie_reply] and
/// [crate::protocol::CryptoServer::handle_msg_under_load].
///
/// [Envelope] and [InitHello] contain some extra examples on how to use structures from the [::zerocopy] crate.
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
pub struct CookieReplyInner {
@@ -120,9 +345,23 @@ pub struct CookieReplyInner {
/// Session ID of the sender (initiator)
pub sid: [u8; 4],
/// Encrypted cookie with authenticated initiator `mac`
pub cookie_encrypted: [u8; xaead::NONCE_LEN + COOKIE_SIZE + xaead::TAG_LEN],
pub cookie_encrypted: [u8; XAead::NONCE_LEN + COOKIE_SIZE + XAead::TAG_LEN],
}
/// Specialized message for use in the cookie mechanism.
///
/// This just brings [CookieReplyInner] up to the size
/// of [InitHello] to avoid amplification Denial of Service attacks.
///
/// See the [whitepaper](https://rosenpass.eu/whitepaper.pdf) ([/papers/whitepaper.md] in this repository) for details.
///
/// # Examples
///
/// To understand how the biscuit is used, it is best to read
/// the code of [crate::protocol::CryptoServer::handle_cookie_reply] and
/// [crate::protocol::CryptoServer::handle_msg_under_load].
///
/// [Envelope] and [InitHello] contain some extra examples on how to use structures from the [::zerocopy] crate.
#[repr(packed)]
#[derive(AsBytes, FromBytes, FromZeroes)]
pub struct CookieReply {
@@ -130,33 +369,46 @@ pub struct CookieReply {
pub padding: [u8; size_of::<Envelope<InitHello>>() - size_of::<CookieReplyInner>()],
}
// Traits /////////////////////////////////////////////////////////////////////
pub trait WireMsg: std::fmt::Debug {
const MSG_TYPE: MsgType;
const MSG_TYPE_U8: u8 = Self::MSG_TYPE as u8;
const BYTES: usize;
}
// Constants //////////////////////////////////////////////////////////////////
pub const SESSION_ID_LEN: usize = 4;
pub const BISCUIT_ID_LEN: usize = 12;
pub const WIRE_ENVELOPE_LEN: usize = 1 + 3 + 16 + 16; // TODO verify this
/// Size required to fit any message in binary form
pub const MAX_MESSAGE_LEN: usize = 2500; // TODO fix this
/// Recognized message types
///
/// # Examples
///
/// ```
/// use rosenpass::msgs::MsgType;
/// use rosenpass::msgs::MsgType as M;
///
/// let values = [M::InitHello, M::RespHello, M::InitConf, M::EmptyData, M::CookieReply];
/// let values_u8 = values.map(|v| -> u8 { v.into() });
///
/// // Can be converted to and from u8 using [::std::convert::Into] or [::std::convert::From]
/// for v in values.iter().copied() {
/// let v_u8 : u8 = v.into();
/// let v2 : MsgType = v_u8.try_into()?;
/// assert_eq!(v, v2);
/// }
///
/// // Converting an unsupported type produces an error
/// let invalid_values = (u8::MIN..=u8::MAX)
/// .filter(|v| !values_u8.contains(v));
/// for v in invalid_values {
/// let res : Result<MsgType, _> = v.try_into();
/// assert!(res.is_err());
/// }
///
/// Ok::<(), anyhow::Error>(())
/// ```
#[repr(u8)]
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
pub enum MsgType {
/// MsgType for [InitHello]
InitHello = 0x81,
/// MsgType for [RespHello]
RespHello = 0x82,
/// MsgType for [InitConf]
InitConf = 0x83,
/// MsgType for [EmptyData]
EmptyData = 0x84,
DataMsg = 0x85,
/// MsgType for [CookieReply]
CookieReply = 0x86,
}
@@ -169,7 +421,6 @@ impl TryFrom<u8> for MsgType {
0x82 => MsgType::RespHello,
0x83 => MsgType::InitConf,
0x84 => MsgType::EmptyData,
0x85 => MsgType::DataMsg,
0x86 => MsgType::CookieReply,
_ => return Err(RosenpassError::InvalidMessageType(value)),
})
@@ -182,16 +433,11 @@ impl From<MsgType> for u8 {
}
}
/// length in bytes of an unencrypted Biscuit (plain text)
pub const BISCUIT_PT_LEN: usize = size_of::<Biscuit>();
/// Length in bytes of an encrypted Biscuit (cipher text)
pub const BISCUIT_CT_LEN: usize = BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG_LEN;
#[cfg(test)]
mod test_constants {
use crate::msgs::{BISCUIT_CT_LEN, BISCUIT_PT_LEN};
use rosenpass_ciphers::{xaead, KEY_LEN};
use rosenpass_cipher_traits::primitives::Aead as _;
use rosenpass_ciphers::{XAead, KEY_LEN};
#[test]
fn sodium_keysize() {
@@ -207,7 +453,7 @@ mod test_constants {
fn biscuit_ct_len() {
assert_eq!(
BISCUIT_CT_LEN,
BISCUIT_PT_LEN + xaead::NONCE_LEN + xaead::TAG_LEN
BISCUIT_PT_LEN + XAead::NONCE_LEN + XAead::TAG_LEN
);
}
}

View File

@@ -1,3 +1,5 @@
use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey};
use crate::config::ProtocolVersion;
use rosenpass_util::{
build::Build,
mem::{DiscardResultExt, SwapWithDefaultExt},
@@ -5,32 +7,100 @@ use rosenpass_util::{
};
use thiserror::Error;
use super::{CryptoServer, PeerPtr, SPk, SSk, SymKey};
#[derive(Debug, Clone)]
/// A pair of matching public/secret keys used to launch the crypto server.
///
/// # Examples
///
/// Decomposing a key pair into its individual components, then recreating it:
///
/// ```rust
/// use rosenpass::protocol::Keypair;
///
/// // We have to define the security policy before using Secrets.
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
/// secret_policy_use_only_malloc_secrets();
///
/// let random_pair = Keypair::random();
/// let random_copy = random_pair.clone();
/// let (sk_copy, pk_copy) = random_copy.into_parts();
///
/// // Re-assemble the key pair from the original secret/public key
/// // Note that it doesn't have to be the exact same keys;
/// // you could just as easily use a completely different pair here
/// let reconstructed_pair = Keypair::from_parts((sk_copy, pk_copy));
///
/// assert_eq!(random_pair.sk.secret(), reconstructed_pair.sk.secret());
/// assert_eq!(random_pair.pk, reconstructed_pair.pk);
/// ```
pub struct Keypair {
/// Secret key matching the crypto server's public key.
pub sk: SSk,
/// Public key identifying the crypto server instance.
pub pk: SPk,
}
// TODO: We need a named tuple derive
impl Keypair {
/// Creates a new key pair from the given secret/public key components.
///
/// # Example
///
/// ```rust
/// use rosenpass::protocol::{Keypair, SSk, SPk};
///
/// // We have to define the security policy before using Secrets.
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
/// secret_policy_use_only_malloc_secrets();
///
/// let random_sk = SSk::random();
/// let random_pk = SPk::random();
/// let random_pair = Keypair::new(random_sk.clone(), random_pk.clone());
///
/// assert_eq!(random_sk.secret(), random_pair.sk.secret());
/// assert_eq!(random_pk, random_pair.pk);
/// ```
pub fn new(sk: SSk, pk: SPk) -> Self {
Self { sk, pk }
}
/// Creates a new "empty" key pair. All bytes are initialized to zero.
///
/// See [SSk:zero()][crate::protocol::SSk::zero] and [SPk:zero()][crate::protocol::SPk::zero], respectively.
///
/// # Example
///
/// ```rust
/// use rosenpass::protocol::{Keypair, SSk, SPk};
///
/// // We have to define the security policy before using Secrets.
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
/// secret_policy_use_only_malloc_secrets();
///
/// let zero_sk = SSk::zero();
/// let zero_pk = SPk::zero();
/// let zero_pair = Keypair::zero();
///
/// assert_eq!(zero_sk.secret(), zero_pair.sk.secret());
/// assert_eq!(zero_pk, zero_pair.pk);
/// ```
pub fn zero() -> Self {
Self::new(SSk::zero(), SPk::zero())
}
/// Creates a new (securely-)random key pair. The mechanism is described in [rosenpass_secret_memory::Secret].
///
/// See [SSk:random()][crate::protocol::SSk::random] and [SPk:random()][crate::protocol::SPk::random], respectively.
pub fn random() -> Self {
Self::new(SSk::random(), SPk::random())
}
/// Creates a new key pair from the given public/secret key components.
pub fn from_parts(parts: (SSk, SPk)) -> Self {
Self::new(parts.0, parts.1)
}
/// Deconstructs the key pair, yielding the individual public/secret key components.
pub fn into_parts(self) -> (SSk, SPk) {
(self.sk, self.pk)
}
@@ -38,25 +108,78 @@ impl Keypair {
#[derive(Error, Debug)]
#[error("PSK already set in BuildCryptoServer")]
/// Error indicating that the PSK is already set.
/// Unused in the current version of the protocol.
pub struct PskAlreadySet;
#[derive(Error, Debug)]
#[error("Keypair already set in BuildCryptoServer")]
/// Error type indicating that the public/secret key pair has already been set.
pub struct KeypairAlreadySet;
#[derive(Error, Debug)]
#[error("Can not construct CryptoServer: Missing keypair")]
/// Error type indicating that no public/secret key pair has been provided.
pub struct MissingKeypair;
#[derive(Debug, Default)]
/// Builder for setting up a [CryptoServer] (with deferred initialization).
///
/// There are multiple ways of creating a crypto server:
///
/// 1. Provide the key pair at initialization time (using [CryptoServer::new][crate::protocol::CryptoServer::new])
/// 2. Provide the key pair at a later time (using [BuildCryptoServer::empty])
///
/// With BuildCryptoServer, you can gradually configure parameters as they become available.
/// This may be useful when they depend on runtime conditions or have to be fetched asynchronously.
/// It's possible to use the builder multiple times; it then serves as a "blueprint" for new
/// instances, several of which may be spawned with the same base configuration (or variations thereof).
///
/// Note that the server won't actually launch without a key pair (expect a [MissingKeypair] error).
/// The setup will be much simplified if one is provided, at the cost of some flexibility.
/// It's however possible to defer this step in case your application requires it.
///
/// For additional details or examples, see [AppServer::crypto_site][crate::app_server::AppServer::crypto_site] and [ConstructionSite][rosenpass_util::build::ConstructionSite].
///
/// # Example
///
/// ```rust
/// use rosenpass_util::build::Build;
/// use rosenpass::protocol::{BuildCryptoServer, Keypair, PeerParams, SPk, SymKey};
/// use rosenpass::config::ProtocolVersion;
///
/// // We have to define the security policy before using Secrets.
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
/// secret_policy_use_only_malloc_secrets();
///
/// let keypair = Keypair::random();
/// let peer1 = PeerParams { psk: Some(SymKey::random()), pk: SPk::random(), protocol_version: ProtocolVersion::V02 };
/// let peer2 = PeerParams { psk: None, pk: SPk::random(), protocol_version: ProtocolVersion::V02 };
///
/// let mut builder = BuildCryptoServer::new(Some(keypair.clone()), vec![peer1]);
/// builder.add_peer(peer2.psk.clone(), peer2.pk, ProtocolVersion::V02);
///
/// let server = builder.build().expect("build failed");
/// assert_eq!(server.peers.len(), 2);
/// assert_eq!(server.sskm.secret(), keypair.sk.secret());
/// assert_eq!(server.spkm, keypair.pk);
/// ```
pub struct BuildCryptoServer {
/// The key pair (secret/public key) identifying the crypto server instance.
pub keypair: Option<Keypair>,
/// A list of network peers that should be registered when launching the server.
pub peers: Vec<PeerParams>,
}
impl Build<CryptoServer> for BuildCryptoServer {
type Error = anyhow::Error;
/// Creates a crypto server, adding all peers that have previously been registered.
///
/// You must provide a key pair at the time of instantiation.
/// If the list of peers is outdated, building the server will fail.
///
/// In this case, make sure to remove or re-add any peers that may have changed.
fn build(self) -> Result<CryptoServer, Self::Error> {
let Some(Keypair { sk, pk }) = self.keypair else {
return Err(MissingKeypair)?;
@@ -64,8 +187,16 @@ impl Build<CryptoServer> for BuildCryptoServer {
let mut srv = CryptoServer::new(sk, pk);
for (idx, PeerParams { psk, pk }) in self.peers.into_iter().enumerate() {
let PeerPtr(idx2) = srv.add_peer(psk, pk)?;
for (
idx,
PeerParams {
psk,
pk,
protocol_version,
},
) in self.peers.into_iter().enumerate()
{
let PeerPtr(idx2) = srv.add_peer(psk, pk, protocol_version.into())?;
assert!(idx == idx2, "Peer id changed during CryptoServer construction from {idx} to {idx2}. This is a developer error.")
}
@@ -74,20 +205,34 @@ impl Build<CryptoServer> for BuildCryptoServer {
}
#[derive(Debug)]
/// Cryptographic key(s) identifying the connected [peer][crate::protocol::Peer] ("client")
/// for a given session that is being managed by the crypto server.
///
/// Each peer must be identified by a [public key (SPk)][crate::protocol::SPk].
/// Optionally, a [symmetric key (SymKey)][crate::protocol::SymKey]
/// can be provided when setting up the connection.
/// For more information on the intended usage and security considerations, see [Peer::psk][crate::protocol::Peer::psk] and [Peer::spkt][crate::protocol::Peer::spkt].
pub struct PeerParams {
/// Pre-shared (symmetric) encryption keys that should be used with this peer.
pub psk: Option<SymKey>,
/// Public key identifying the peer.
pub pk: SPk,
/// The used protocol version.
pub protocol_version: ProtocolVersion,
}
impl BuildCryptoServer {
/// Creates a new builder instance using the given key pair and peer list.
pub fn new(keypair: Option<Keypair>, peers: Vec<PeerParams>) -> Self {
Self { keypair, peers }
}
/// Creates an "incomplete" builder instance, without assigning a key pair.
pub fn empty() -> Self {
Self::new(None, Vec::new())
}
/// Creates a builder instance from the given key pair and peer list components.
pub fn from_parts(parts: (Option<Keypair>, Vec<PeerParams>)) -> Self {
Self {
keypair: parts.0,
@@ -95,32 +240,182 @@ impl BuildCryptoServer {
}
}
/// Deconstructs the current builder instance, taking ownership of its key pair and peer list.
///
/// Replaces all parameters with their default values, which allows extracting them
/// while leaving the builder in a reusable state.
pub fn take_parts(&mut self) -> (Option<Keypair>, Vec<PeerParams>) {
(self.keypair.take(), self.peers.swap_with_default())
}
/// Deconstructs the builder instance, yielding the assigned key pair and peer list.
pub fn into_parts(mut self) -> (Option<Keypair>, Vec<PeerParams>) {
self.take_parts()
}
/// Creates a new builder instance, assigning the given keypair to it.
///
/// Note that only one key pair can be assigned (expect [KeypairAlreadySet] on failure).
///
/// # Examples
///
/// ## Adding key pairs to an existing builder
///
/// ```rust
/// // We have to define the security policy before using Secrets.
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
/// secret_policy_use_only_malloc_secrets();
///
/// use rosenpass_util::build::Build;
/// use rosenpass::protocol::{BuildCryptoServer, Keypair};
///
/// // Deferred initialization: Create builder first, add the key pair later
/// let mut builder = BuildCryptoServer::empty();
/// // Do something with the builder ...
///
/// // Quite some time may have passed (network/disk IO, runtime events, ...)
/// // Now we've got a key pair that should be added to the configuration
/// let keypair = Keypair::random();
/// builder.with_keypair(keypair.clone()).expect("build with key pair failed");
///
/// // New server instances can now make use of the assigned key pair
/// let server = builder.build().expect("build failed");
/// assert_eq!(server.sskm.secret(), keypair.sk.secret());
/// assert_eq!(server.spkm, keypair.pk);
/// ```
///
/// ## Basic error handling: Re-assigning key pairs
///
/// ```rust
/// // We have to define the security policy before using Secrets.
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
/// secret_policy_use_only_malloc_secrets();
///
/// use rosenpass_util::build::Build;
/// use rosenpass::protocol::{BuildCryptoServer, Keypair, KeypairAlreadySet};
///
/// // In this case, we'll create a functional builder from its various components
/// // These could be salvaged from another builder, or obtained from disk/network (etc.)
/// let keypair = Keypair::random();
/// let mut builder = BuildCryptoServer::from_parts((Some(keypair.clone()), Vec::new()));
///
/// // The builder has already been assigned a key pair, so this won't work
/// let err = builder.with_keypair(keypair).expect_err("should fail to reassign key pair");
/// assert!(matches!(err, KeypairAlreadySet));
/// ```
pub fn with_keypair(&mut self, keypair: Keypair) -> Result<&mut Self, KeypairAlreadySet> {
ensure_or(self.keypair.is_none(), KeypairAlreadySet)?;
self.keypair.insert(keypair).discard_result();
Ok(self)
}
pub fn with_added_peer(&mut self, psk: Option<SymKey>, pk: SPk) -> &mut Self {
/// Creates a new builder instance, adding a new entry to the list of registered peers.
///
/// # Example
///
/// Adding peers to an existing builder:
///
/// ```rust
/// use rosenpass::config::ProtocolVersion;
/// // We have to define the security policy before using Secrets.
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
/// secret_policy_use_only_malloc_secrets();
///
/// use rosenpass_util::build::Build;
/// use rosenpass::protocol::{BuildCryptoServer, Keypair, SymKey, SPk};
///
/// // Deferred initialization: Create builder first, add some peers later
/// let keypair_option = Some(Keypair::random());
/// let mut builder = BuildCryptoServer::new(keypair_option, Vec::new());
/// assert!(builder.peers.is_empty());
///
/// // Do something with the builder ...
///
/// // Quite some time may have passed (network/disk IO, runtime events, ...)
/// // Now we've found a peer that should be added to the configuration
/// let pre_shared_key = SymKey::random();
/// let public_key = SPk::random();
/// builder.with_added_peer(Some(pre_shared_key.clone()), public_key.clone(), ProtocolVersion::V02);
///
/// // New server instances will then start with the peer being registered already
/// let server = builder.build().expect("build failed");
/// assert_eq!(server.peers.len(), 1);
/// let peer = &server.peers[0];
/// let peer_psk = Some(peer.psk.clone()).expect("PSK is None");
/// assert_eq!(peer.spkt, public_key);
/// assert_eq!(peer_psk.secret(), pre_shared_key.secret());
/// ```
pub fn with_added_peer(
&mut self,
psk: Option<SymKey>,
pk: SPk,
protocol_version: ProtocolVersion,
) -> &mut Self {
// TODO: Check here already whether peer was already added
self.peers.push(PeerParams { psk, pk });
self.peers.push(PeerParams {
psk,
pk,
protocol_version,
});
self
}
pub fn add_peer(&mut self, psk: Option<SymKey>, pk: SPk) -> PeerPtr {
/// Add a new entry to the list of registered peers, with or without a pre-shared key.
pub fn add_peer(
&mut self,
psk: Option<SymKey>,
pk: SPk,
protocol_version: ProtocolVersion,
) -> PeerPtr {
let id = PeerPtr(self.peers.len());
self.with_added_peer(psk, pk);
self.with_added_peer(psk, pk, protocol_version);
id
}
/// Creates a new builder, taking ownership of another instance's key pair and peer list.
/// Allows duplicating the current set of launch parameters, which can then be used to
/// start multiple servers with the exact same configuration (or variants using it as a base).
///
/// # Example
///
/// Extracting the server configuration from a builder:
///
/// ```rust
/// // We have to define the security policy before using Secrets.
/// use rosenpass::config::ProtocolVersion;
/// use rosenpass::hash_domains::protocol;
/// use rosenpass_secret_memory::secret_policy_use_only_malloc_secrets;
/// secret_policy_use_only_malloc_secrets();
///
/// use rosenpass_util::build::Build;
/// use rosenpass::protocol::{BuildCryptoServer, Keypair, SymKey, SPk};
///
/// let keypair = Keypair::random();
/// let peer_pk = SPk::random();
/// let mut builder = BuildCryptoServer::new(Some(keypair.clone()), vec![]);
/// builder.add_peer(None, peer_pk, ProtocolVersion::V02);
///
/// // Extract configuration parameters from the decomissioned builder
/// let (keypair_option, peers) = builder.take_parts();
/// let extracted_keypair = keypair_option.unwrap();
/// assert_eq!(extracted_keypair.sk.secret(), keypair.sk.secret());
/// assert_eq!(extracted_keypair.pk, keypair.pk);
/// assert_eq!(peers.len(), 1);
///
/// // Now we can create a new builder with the same configuration
/// let parts = (Some(extracted_keypair), peers);
/// let mut reassembled_builder = BuildCryptoServer::from_parts(parts);
/// let new_builder = reassembled_builder.emancipate();
///
/// // Do something with the new builder ...
///
/// // ... and now, deconstruct this one as well - still using the same parts
/// let (keypair_option, peers) = new_builder.into_parts();
/// let extracted_keypair = keypair_option.unwrap();
/// assert_eq!(extracted_keypair.sk.secret(), keypair.sk.secret());
/// assert_eq!(extracted_keypair.pk, keypair.pk);
/// assert_eq!(peers.len(), 1);
/// ```
pub fn emancipate(&mut self) -> Self {
Self::from_parts(self.take_parts())
}

View File

@@ -1,3 +1,80 @@
//! Module containing the cryptographic protocol implementation
//!
//! # Overview
//!
//! The most important types in this module probably are [PollResult]
//! & [CryptoServer]. Once a [CryptoServer] is created, the server is
//! provided with new messages via the [CryptoServer::handle_msg] method.
//! The [CryptoServer::poll] method can be used to let the server work, which
//! will eventually yield a [PollResult]. Said [PollResult] contains
//! prescriptive activities to be carried out. [CryptoServer::osk] can than
//! be used to extract the shared key for two peers, once a key-exchange was
//! successful.
//!
//! TODO explain briefly the role of epki
//!
//! # Example Handshake
//!
//! This example illustrates a minimal setup for a key-exchange between two
//! [CryptoServer]s; this is what we use for some testing purposes but it is not
//! what should be used in a real world application, as timing-based events
//! are handled by [CryptoServer::poll].
//!
//! See [CryptoServer::poll] on how to use crypto server in polling mode for production usage.
//!
//! ```
//! use std::ops::DerefMut;
//! use rosenpass_secret_memory::policy::*;
//! use rosenpass_cipher_traits::primitives::Kem;
//! use rosenpass_ciphers::StaticKem;
//! use rosenpass::{
//! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey},
//! };
//! # fn main() -> anyhow::Result<()> {
//! // Set security policy for storing secrets
//!
//! use rosenpass::protocol::ProtocolVersion;
//! secret_policy_try_use_memfd_secrets();
//!
//! // 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.deref_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.deref_mut())?;
//!
//! // initialize server and a pre-shared key
//! let psk = SymKey::random();
//! let mut a = CryptoServer::new(peer_a_sk, peer_a_pk.clone());
//! let mut b = CryptoServer::new(peer_b_sk, peer_b_pk.clone());
//!
//! // introduce peers to each other
//! a.add_peer(Some(psk.clone()), peer_b_pk, ProtocolVersion::V03)?;
//! b.add_peer(Some(psk), peer_a_pk, ProtocolVersion::V03)?;
//!
//! // declare buffers for message exchange
//! let (mut a_buf, mut b_buf) = (MsgBuf::zero(), MsgBuf::zero());
//!
//! // let a initiate a handshake
//! let mut maybe_len = Some(a.initiate_handshake(PeerPtr(0), a_buf.as_mut_slice())?);
//!
//! // let a and b communicate
//! while let Some(len) = maybe_len {
//! maybe_len = b.handle_msg(&a_buf[..len], &mut b_buf[..])?.resp;
//! std::mem::swap(&mut a, &mut b);
//! std::mem::swap(&mut a_buf, &mut b_buf);
//! }
//!
//! // all done! Extract the shared keys and ensure they are identical
//! let a_key = a.osk(PeerPtr(0))?;
//! let b_key = b.osk(PeerPtr(0))?;
//! assert_eq!(a_key.secret(), b_key.secret(),
//! "the key exchanged failed to establish a shared secret");
//! # Ok(())
//! # }
//! ```
mod build_crypto_server;
#[allow(clippy::module_inception)]
mod protocol;

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,8 @@ use rosenpass::api::{
self, add_listen_socket_response_status, add_psk_broker_response_status,
supply_keypair_response_status,
};
use rosenpass::config::ProtocolVersion;
use rosenpass::protocol::SymKey;
use rosenpass_util::{
b64::B64Display,
file::LoadValueB64,
@@ -27,19 +29,35 @@ use std::os::fd::{AsFd, AsRawFd};
use tempfile::TempDir;
use zerocopy::AsBytes;
use rosenpass::protocol::SymKey;
struct KillChild(std::process::Child);
impl Drop for KillChild {
fn drop(&mut self) {
self.0.kill().discard_result();
self.0.wait().discard_result()
use rustix::process::{kill_process, Pid, Signal::Term};
let pid = Pid::from_child(&self.0);
// We seriously need to start handling signals with signalfd, our current signal handling
// system is a bit broken; there is probably a few functions that just restart on EINTR
// so the signal is absorbed
loop {
kill_process(pid, Term).discard_result();
if self.0.try_wait().unwrap().is_some() {
break;
}
}
}
}
#[test]
fn api_integration_api_setup() -> anyhow::Result<()> {
fn api_integration_api_setup_v02() -> anyhow::Result<()> {
api_integration_api_setup(ProtocolVersion::V02)
}
#[test]
fn api_integration_api_setup_v03() -> anyhow::Result<()> {
api_integration_api_setup(ProtocolVersion::V03)
}
fn api_integration_api_setup(protocol_version: ProtocolVersion) -> anyhow::Result<()> {
rosenpass_secret_memory::policy::secret_policy_use_only_malloc_secrets();
let dir = TempDir::with_prefix("rosenpass-api-integration-test")?;
@@ -87,6 +105,7 @@ fn api_integration_api_setup() -> anyhow::Result<()> {
peer: format!("{}", peer_b_wg_peer_id.fmt_b64::<8129>()),
extra_params: vec![],
}),
protocol_version: protocol_version.clone(),
}],
};
@@ -107,6 +126,7 @@ fn api_integration_api_setup() -> anyhow::Result<()> {
endpoint: Some(peer_a_endpoint.to_owned()),
pre_shared_key: None,
wg: None,
protocol_version: protocol_version.clone(),
}],
};
@@ -153,7 +173,6 @@ fn api_integration_api_setup() -> anyhow::Result<()> {
peer_b.config_file_path.to_str().context("")?,
])
.stdin(Stdio::null())
.stderr(Stdio::null())
.stdout(Stdio::piped())
.spawn()?,
);

Some files were not shown because too many files have changed in this diff Show More