mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-18 13:24:38 +03:00
Compare commits
60 Commits
dev/refine
...
rosenpass-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3901e668cb | ||
|
|
b7444bf9b4 | ||
|
|
0051cbd48e | ||
|
|
27746781c0 | ||
|
|
93439858d1 | ||
|
|
1223048b48 | ||
|
|
932bde39cc | ||
|
|
1d9e62e56b | ||
|
|
3af722a066 | ||
|
|
df60b0bfc3 | ||
|
|
6274c6fcdd | ||
|
|
cd00f023fb | ||
|
|
13563237cb | ||
|
|
447a4f7a44 | ||
|
|
6bac6a59ff | ||
|
|
e5e04c6d95 | ||
|
|
15ce25ccd2 | ||
|
|
1b383d494c | ||
|
|
605b6463ff | ||
|
|
04eb86af87 | ||
|
|
bf850e3072 | ||
|
|
dd39936220 | ||
|
|
b15f17133f | ||
|
|
b50820ecc0 | ||
|
|
f323839967 | ||
|
|
6e15c38254 | ||
|
|
b7a76849b7 | ||
|
|
d2d72143b5 | ||
|
|
1135cd7bbb | ||
|
|
51f04f749f | ||
|
|
37d1326481 | ||
|
|
d0a84294aa | ||
|
|
a98f64c17d | ||
|
|
d6a7ebe88f | ||
|
|
212336728c | ||
|
|
f48a923dbf | ||
|
|
7b5d0f7d66 | ||
|
|
1e37f89e83 | ||
|
|
b997238f42 | ||
|
|
d915e63445 | ||
|
|
53d7996dd3 | ||
|
|
47b4d394ef | ||
|
|
578d9e2eb5 | ||
|
|
d6b83a4a0b | ||
|
|
959cd50ef6 | ||
|
|
6025623aad | ||
|
|
5a67b4708a | ||
|
|
45145cdd9b | ||
|
|
66e696fea3 | ||
|
|
91d0592ad6 | ||
|
|
8ff9b53365 | ||
|
|
067a839d4b | ||
|
|
38835fb0f8 | ||
|
|
a2b177470c | ||
|
|
1c1e38e2f7 | ||
|
|
46383bdc4d | ||
|
|
2805d686e6 | ||
|
|
b274519bad | ||
|
|
3086c7fb93 | ||
|
|
d21e3af1bb |
@@ -33,7 +33,7 @@ let systems_map = {
|
||||
# aarch64-linux
|
||||
|
||||
i686-linux: ubuntu-latest,
|
||||
x86_64-darwin: macos-latest,
|
||||
x86_64-darwin: macos-13,
|
||||
x86_64-linux: ubuntu-latest
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ let runner_setup = [
|
||||
uses: "actions/checkout@v3"
|
||||
}
|
||||
{
|
||||
uses: "cachix/install-nix-action@v21",
|
||||
uses: "cachix/install-nix-action@v22",
|
||||
with: { nix_path: "nixpkgs=channel:nixos-unstable" }
|
||||
}
|
||||
{
|
||||
|
||||
52
.github/workflows/nix.yaml
vendored
52
.github/workflows/nix.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- i686-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
- i686-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -75,12 +75,12 @@ jobs:
|
||||
x86_64-darwin---default:
|
||||
name: Build x86_64-darwin.default
|
||||
runs-on:
|
||||
- macos-latest
|
||||
- macos-13
|
||||
needs:
|
||||
- x86_64-darwin---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -92,13 +92,13 @@ jobs:
|
||||
x86_64-darwin---release-package:
|
||||
name: Build x86_64-darwin.release-package
|
||||
runs-on:
|
||||
- macos-latest
|
||||
- macos-13
|
||||
needs:
|
||||
- x86_64-darwin---rosenpass
|
||||
- x86_64-darwin---rosenpass-oci-image
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -110,11 +110,11 @@ jobs:
|
||||
x86_64-darwin---rosenpass:
|
||||
name: Build x86_64-darwin.rosenpass
|
||||
runs-on:
|
||||
- macos-latest
|
||||
- macos-13
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -126,12 +126,12 @@ jobs:
|
||||
x86_64-darwin---rosenpass-oci-image:
|
||||
name: Build x86_64-darwin.rosenpass-oci-image
|
||||
runs-on:
|
||||
- macos-latest
|
||||
- macos-13
|
||||
needs:
|
||||
- x86_64-darwin---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -143,10 +143,10 @@ jobs:
|
||||
x86_64-darwin---check:
|
||||
name: Run Nix checks on x86_64-darwin
|
||||
runs-on:
|
||||
- macos-latest
|
||||
- macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -163,7 +163,7 @@ jobs:
|
||||
- x86_64-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -180,7 +180,7 @@ jobs:
|
||||
- x86_64-linux---proverif-patched
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -196,7 +196,7 @@ jobs:
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -210,11 +210,11 @@ jobs:
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
needs:
|
||||
- x86_64-linux---rosenpass-static
|
||||
- x86_64-linux---rosenpass-static-oci-image
|
||||
- x86_64-linux---rosenpass-static
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -230,7 +230,7 @@ jobs:
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -247,7 +247,7 @@ jobs:
|
||||
- x86_64-linux---rosenpass
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -263,7 +263,7 @@ jobs:
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -280,7 +280,7 @@ jobs:
|
||||
- x86_64-linux---rosenpass-static
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -296,7 +296,7 @@ jobs:
|
||||
needs: []
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -311,7 +311,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -326,7 +326,7 @@ jobs:
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
|
||||
51
.github/workflows/qc.yaml
vendored
51
.github/workflows/qc.yaml
vendored
@@ -17,6 +17,14 @@ jobs:
|
||||
with:
|
||||
args: --check .
|
||||
|
||||
shellcheck:
|
||||
name: Shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
|
||||
cargo-audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -66,3 +74,46 @@ jobs:
|
||||
# - https://github.com/rosenpass/rosenpass/issues/62
|
||||
# - https://github.com/rust-lang/rust/issues/108378
|
||||
- run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items
|
||||
|
||||
cargo-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install libsodium
|
||||
run: sudo apt-get install -y libsodium-dev
|
||||
# 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
|
||||
|
||||
cargo-test-nix-devshell-x86_64-linux:
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- uses: cachix/install-nix-action@v21
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: rosenpass
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- run: nix develop --command cargo test
|
||||
|
||||
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -30,10 +30,10 @@ jobs:
|
||||
x86_64-darwin---release:
|
||||
name: Build release artifacts for x86_64-darwin
|
||||
runs-on:
|
||||
- macos-latest
|
||||
- macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: cachix/install-nix-action@v21
|
||||
- uses: cachix/install-nix-action@v22
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@v12
|
||||
|
||||
17
.gitlab-ci.yml
Normal file
17
.gitlab-ci.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# TODO use CI_JOB_TOKEN once https://gitlab.com/groups/gitlab-org/-/epics/6310 is fixed
|
||||
pull-from-gh:
|
||||
only: ["schedules"]
|
||||
variables:
|
||||
REMOTE: "https://github.com/rosenpass/rosenpass.git"
|
||||
LOCAL: " git@gitlab.com:rosenpass/rosenpass.git"
|
||||
GIT_STRATEGY: none
|
||||
before_script:
|
||||
- mkdir ~/.ssh/
|
||||
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
|
||||
- echo "$REPO_SSH_KEY" > ~/.ssh/id_ed25519
|
||||
- chmod 600 --recursive ~/.ssh/
|
||||
- git config --global user.email "ci@gitlab.com"
|
||||
- git config --global user.name "CI"
|
||||
script:
|
||||
- git clone --mirror $REMOTE rosenpass
|
||||
- cd rosenpass && git push --mirror $LOCAL
|
||||
825
Cargo.lock
generated
825
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
43
Cargo.toml
43
Cargo.toml
@@ -1,40 +1,7 @@
|
||||
[package]
|
||||
name = "rosenpass"
|
||||
version = "0.1.2-rc.4"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Build post-quantum-secure VPNs with WireGuard!"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
|
||||
[[bench]]
|
||||
name = "handshake"
|
||||
harness = false
|
||||
members = [
|
||||
"rosenpass",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.21.1"
|
||||
static_assertions = "1.1.0"
|
||||
memoffset = "0.9.0"
|
||||
libsodium-sys-stable = { version = "1.19.28", features = ["use-pkg-config"] }
|
||||
oqs-sys = { version = "0.7.2", default-features = false, features = ['classic_mceliece', 'kyber'] }
|
||||
lazy_static = "1.4.0"
|
||||
thiserror = "1.0.40"
|
||||
paste = "1.0.12"
|
||||
log = { version = "0.4.17", optional = true }
|
||||
env_logger = { version = "0.10.0", optional = true }
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
toml = "0.7.4"
|
||||
clap = { version = "4.3.0", features = ["derive"] }
|
||||
mio = { version = "0.8.6", features = ["net", "os-poll"] }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.71"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.4.0"
|
||||
test_bin = "0.4.0"
|
||||
|
||||
[features]
|
||||
default = ["log", "env_logger"]
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
performs cryptographic key exchanges that are secure against quantum-computers
|
||||
and outputs the keys.
|
||||
These keys can then be passed to various services such as wireguard or other
|
||||
vpn services as pre-shared-keys to achieve security against attackers with
|
||||
and then outputs the keys.
|
||||
These keys can then be passed to various services, such as wireguard or other
|
||||
vpn services, as pre-shared-keys to achieve security against attackers with
|
||||
quantum computers.
|
||||
.Pp
|
||||
This is a research project and quantum computers are not thought to become
|
||||
practical in less than ten years.
|
||||
practical in fewer than ten years.
|
||||
If you are not specifically tasked with developing post-quantum secure systems,
|
||||
you probably do not need this tool.
|
||||
.Ss COMMANDS
|
||||
@@ -31,7 +31,7 @@ file secret!
|
||||
Start a process to exchange keys with the specified peers.
|
||||
You should specify at least one peer.
|
||||
.Pp
|
||||
It's
|
||||
Its
|
||||
.Ar OPTIONS
|
||||
are as follows:
|
||||
.Bl -tag -width Ds
|
||||
@@ -39,7 +39,7 @@ are as follows:
|
||||
Instructs
|
||||
.Nm
|
||||
to listen on the specified interface and port.
|
||||
By default
|
||||
By default,
|
||||
.Nm
|
||||
will listen on all interfaces and select a random port.
|
||||
.It Ar verbose
|
||||
|
||||
4
doc/rp.1
4
doc/rp.1
@@ -59,6 +59,10 @@ listening on the provided IP and port combination, allowing connections from
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh EXAMPLES
|
||||
In this example, we will assume that the server has an interface bound to
|
||||
192.168.0.1, that accepts incoming connections on port 9999/UDP for Rosenpass
|
||||
and port 10000/UDP for WireGuard.
|
||||
.Pp
|
||||
To create a VPN connection, start by generating secret keys on both hosts.
|
||||
.Bd -literal -offset indent
|
||||
rp genkey server.rosenpass-secret
|
||||
|
||||
30
flake.lock
generated
30
flake.lock
generated
@@ -8,11 +8,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1686291735,
|
||||
"narHash": "sha256-mpq2m6TN3ImqqUqA4u93NvkZu5vH//3spqjmPRbRlvA=",
|
||||
"lastModified": 1699770036,
|
||||
"narHash": "sha256-bZmI7ytPAYLpyFNgj5xirDkKuAniOkj1xHdv5aIJ5GM=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "6e6a94c4d0cac4821b6452fbae46609b89a8ddcf",
|
||||
"rev": "81ab0b4f7ae9ebb57daa0edf119c4891806e4d3a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -26,11 +26,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1685518550,
|
||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -46,11 +46,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1679567394,
|
||||
"narHash": "sha256-ZvLuzPeARDLiQUt6zSZFGOs+HZmE+3g4QURc8mkBsfM=",
|
||||
"lastModified": 1698420672,
|
||||
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "88cd22380154a2c36799fe8098888f0f59861a15",
|
||||
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -61,11 +61,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1686237827,
|
||||
"narHash": "sha256-fAZB+Zkcmc+qlauiFnIH9+2qgwM0NO/ru5pWEw3tDow=",
|
||||
"lastModified": 1698846319,
|
||||
"narHash": "sha256-4jyW/dqFBVpWFnhl0nvP6EN4lP7/ZqPxYRjl6var0Oc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "81ed90058a851eb73be835c770e062c6938c8a9e",
|
||||
"rev": "34bdaaf1f0b7fb6d9091472edc968ff10a8c2857",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -84,11 +84,11 @@
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1686239338,
|
||||
"narHash": "sha256-c6Mm7UnDf3j3akY3YB3rELFA76QRbB8ttSBsh00LWi0=",
|
||||
"lastModified": 1699715108,
|
||||
"narHash": "sha256-yPozsobJU55gj+szgo4Lpcg1lHvGQYAT6Y4MrC80mWE=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "9c03aa1ac2e67051db83a85baf3cfee902e4dd84",
|
||||
"rev": "5fcf5289e726785d20d3aa4d13d90a43ed248e83",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
23
flake.nix
23
flake.nix
@@ -55,14 +55,13 @@
|
||||
};
|
||||
|
||||
# parsed Cargo.toml
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./rosenpass/Cargo.toml);
|
||||
|
||||
# source files relevant for rust
|
||||
src = pkgs.lib.sourceByRegex ./. [
|
||||
"Cargo\\.(toml|lock)"
|
||||
"build.rs"
|
||||
"(src|benches)(/.*\\.(rs|md))?"
|
||||
"rp"
|
||||
src = pkgs.lib.sources.sourceFilesBySuffices ./. [
|
||||
".lock"
|
||||
".rs"
|
||||
".toml"
|
||||
];
|
||||
|
||||
# builds a bin path for all dependencies for the `rp` shellscript
|
||||
@@ -112,6 +111,9 @@
|
||||
version = cargoToml.package.version;
|
||||
inherit src;
|
||||
|
||||
cargoBuildOptions = x: x ++ [ "-p" "rosenpass" ];
|
||||
cargoTestOptions = x: x ++ [ "-p" "rosenpass" ];
|
||||
|
||||
doCheck = true;
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
@@ -157,11 +159,6 @@
|
||||
'';
|
||||
};
|
||||
|
||||
# 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_
|
||||
RUST_MIN_STACK = 8 * 1024 * 1024; # 8 MiB
|
||||
|
||||
# We want to build for a specific target...
|
||||
CARGO_BUILD_TARGET = target;
|
||||
|
||||
@@ -290,7 +287,7 @@
|
||||
packages.proof-proverif = pkgs.stdenv.mkDerivation {
|
||||
name = "rosenpass-proverif-proof";
|
||||
version = "unstable";
|
||||
src = pkgs.lib.sourceByRegex ./. [
|
||||
src = pkgs.lib.sources.sourceByRegex ./. [
|
||||
"analyze.sh"
|
||||
"marzipan(/marzipan.awk)?"
|
||||
"analysis(/.*)?"
|
||||
@@ -309,7 +306,6 @@
|
||||
#
|
||||
devShells.default = pkgs.mkShell {
|
||||
inherit (packages.proof-proverif) CRYPTOVERIF_LIB;
|
||||
inherit (packages.rosenpass) RUST_MIN_STACK;
|
||||
inputsFrom = [ packages.default ];
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cmake # override the fakecmake from the main step above
|
||||
@@ -322,7 +318,6 @@
|
||||
};
|
||||
devShells.coverage = pkgs.mkShell {
|
||||
inputsFrom = [ packages.default ];
|
||||
inherit (packages.rosenpass) RUST_MIN_STACK;
|
||||
nativeBuildInputs = with pkgs; [ inputs.fenix.packages.${system}.complete.toolchain cargo-llvm-cov ];
|
||||
};
|
||||
|
||||
|
||||
@@ -71,6 +71,13 @@ Rosenpass is packaged for more and more distributions, maybe also for the distri
|
||||
|
||||
[](https://repology.org/project/rosenpass/versions)
|
||||
|
||||
# Mirrors
|
||||
|
||||
Don't want to use GitHub or only have an IPv6 connection? Rosenpass has set up two mirrors for this:
|
||||
|
||||
- [NotABug](https://notabug.org/rosenpass/rosenpass)
|
||||
- [GitLab](https://gitlab.com/rosenpass/rosenpass/)
|
||||
|
||||
# Supported by
|
||||
|
||||
Funded through <a href="https://nlnet.nl/">NLNet</a> with financial support for the European Commission's <a href="https://nlnet.nl/assure">NGI Assure</a> program.
|
||||
|
||||
42
rosenpass/Cargo.toml
Normal file
42
rosenpass/Cargo.toml
Normal file
@@ -0,0 +1,42 @@
|
||||
[package]
|
||||
name = "rosenpass"
|
||||
version = "0.2.1-rc.1"
|
||||
authors = ["Karolin Varner <karo@cupdev.net>", "wucke13 <wucke13@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Build post-quantum-secure VPNs with WireGuard!"
|
||||
homepage = "https://rosenpass.eu/"
|
||||
repository = "https://github.com/rosenpass/rosenpass"
|
||||
readme = "readme.md"
|
||||
|
||||
[[bench]]
|
||||
name = "handshake"
|
||||
harness = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.71", features = ["backtrace"] }
|
||||
base64 = "0.21.1"
|
||||
static_assertions = "1.1.0"
|
||||
memoffset = "0.9.0"
|
||||
libsodium-sys-stable = { version = "1.19.28", features = ["use-pkg-config"] }
|
||||
oqs-sys = { version = "0.8", default-features = false, features = ['classic_mceliece', 'kyber'] }
|
||||
lazy_static = "1.4.0"
|
||||
thiserror = "1.0.40"
|
||||
paste = "1.0.12"
|
||||
log = { version = "0.4.17", optional = true }
|
||||
env_logger = { version = "0.10.0", optional = true }
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
toml = "0.7.4"
|
||||
clap = { version = "4.3.0", features = ["derive"] }
|
||||
mio = { version = "0.8.6", features = ["net", "os-poll"] }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = "1.0.71"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.4.0"
|
||||
test_bin = "0.4.0"
|
||||
stacker = "0.1.15"
|
||||
|
||||
[features]
|
||||
default = ["log", "env_logger"]
|
||||
@@ -1,7 +1,8 @@
|
||||
use anyhow::Result;
|
||||
use rosenpass::pqkem::KEM;
|
||||
use rosenpass::{
|
||||
pqkem::{EphemeralKEM, CCAKEM},
|
||||
protocol::{CcaPk, CcaSk, CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, SymKey},
|
||||
pqkem::StaticKEM,
|
||||
protocol::{CryptoServer, HandleMsgResult, MsgBuf, PeerPtr, SPk, SSk, SymKey},
|
||||
sodium::sodium_init,
|
||||
};
|
||||
|
||||
@@ -38,9 +39,9 @@ fn hs(ini: &mut CryptoServer, res: &mut CryptoServer) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn keygen() -> Result<(CcaSk, CcaPk)> {
|
||||
let (mut sk, mut pk) = (CcaSk::zero(), CcaPk::zero());
|
||||
CCAKEM::keygen(sk.secret_mut(), pk.secret_mut())?;
|
||||
fn keygen() -> Result<(SSk, SPk)> {
|
||||
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||
StaticKEM::keygen(sk.secret_mut(), pk.secret_mut())?;
|
||||
Ok((sk, pk))
|
||||
}
|
||||
|
||||
@@ -61,12 +62,12 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
let (mut a, mut b) = make_server_pair().unwrap();
|
||||
c.bench_function("cca_secret_alloc", |bench| {
|
||||
bench.iter(|| {
|
||||
CcaSk::zero();
|
||||
SSk::zero();
|
||||
})
|
||||
});
|
||||
c.bench_function("cca_public_alloc", |bench| {
|
||||
bench.iter(|| {
|
||||
CcaPk::zero();
|
||||
SPk::zero();
|
||||
})
|
||||
});
|
||||
c.bench_function("keygen", |bench| {
|
||||
@@ -21,13 +21,13 @@ fn generate_man() -> String {
|
||||
// This function is purposely stupid and redundant
|
||||
|
||||
let man = render_man("mandoc", "./doc/rosenpass.1");
|
||||
if man.is_ok() {
|
||||
return man.unwrap();
|
||||
if let Ok(man) = man {
|
||||
return man;
|
||||
}
|
||||
|
||||
let man = render_man("groff", "./doc/rosenpass.1");
|
||||
if man.is_ok() {
|
||||
return man.unwrap();
|
||||
if let Ok(man) = man {
|
||||
return man;
|
||||
}
|
||||
|
||||
// TODO: Link to online manual here
|
||||
1
rosenpass/readme.md
Symbolic link
1
rosenpass/readme.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../readme.md
|
||||
@@ -1,4 +1,7 @@
|
||||
use log::{error, info, warn};
|
||||
use anyhow::bail;
|
||||
|
||||
use anyhow::Result;
|
||||
use log::{debug, error, info, warn};
|
||||
use mio::Interest;
|
||||
use mio::Token;
|
||||
|
||||
@@ -16,15 +19,14 @@ use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use std::slice;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::util::fopen_w;
|
||||
use crate::RosenpassError;
|
||||
use crate::{
|
||||
config::Verbosity,
|
||||
protocol::{CryptoServer, MsgBuf, PeerPtr, SPk, SSk, SymKey, Timing},
|
||||
util::{b64_writer, fmt_b64},
|
||||
Result,
|
||||
};
|
||||
|
||||
const IPV4_ANY_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
|
||||
@@ -97,8 +99,8 @@ impl SocketPtr {
|
||||
&mut srv.sockets[self.0]
|
||||
}
|
||||
|
||||
pub fn send_to(&self, srv: &AppServer, buf: &[u8], addr: SocketAddr) -> Result<()> {
|
||||
self.get(srv).send_to(&buf, addr)?;
|
||||
pub fn send_to(&self, srv: &AppServer, buf: &[u8], addr: SocketAddr) -> anyhow::Result<()> {
|
||||
self.get(srv).send_to(buf, addr)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -180,7 +182,7 @@ impl Endpoint {
|
||||
}
|
||||
|
||||
/// Start endpoint discovery from a hostname
|
||||
pub fn discovery_from_hostname(hostname: String) -> Result<Self> {
|
||||
pub fn discovery_from_hostname(hostname: String) -> anyhow::Result<Self> {
|
||||
let host = HostPathDiscoveryEndpoint::lookup(hostname)?;
|
||||
Ok(Endpoint::Discovery(host))
|
||||
}
|
||||
@@ -210,7 +212,7 @@ impl Endpoint {
|
||||
Some(Self::discovery_from_addresses(addrs))
|
||||
}
|
||||
|
||||
pub fn send(&self, srv: &AppServer, buf: &[u8]) -> Result<()> {
|
||||
pub fn send(&self, srv: &AppServer, buf: &[u8]) -> anyhow::Result<()> {
|
||||
use Endpoint::*;
|
||||
match self {
|
||||
SocketBoundAddress { socket, addr } => socket.send_to(srv, buf, *addr),
|
||||
@@ -269,7 +271,7 @@ impl HostPathDiscoveryEndpoint {
|
||||
}
|
||||
|
||||
/// Lookup a hostname
|
||||
pub fn lookup(hostname: String) -> Result<Self> {
|
||||
pub fn lookup(hostname: String) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
addresses: ToSocketAddrs::to_socket_addrs(&hostname)?.collect(),
|
||||
scouting_state: Cell::new((0, 0)),
|
||||
@@ -290,16 +292,16 @@ impl HostPathDiscoveryEndpoint {
|
||||
/// Attempt to reach the host
|
||||
///
|
||||
/// Will round-robin-try different socket-ip-combinations on each call.
|
||||
pub fn send_scouting(&self, srv: &AppServer, buf: &[u8]) -> Result<()> {
|
||||
pub fn send_scouting(&self, srv: &AppServer, buf: &[u8]) -> anyhow::Result<()> {
|
||||
let (addr_off, sock_off) = self.scouting_state.get();
|
||||
|
||||
let mut addrs = (&self.addresses)
|
||||
let mut addrs = (self.addresses)
|
||||
.iter()
|
||||
.enumerate()
|
||||
.cycle()
|
||||
.skip(addr_off)
|
||||
.take(self.addresses.len());
|
||||
let mut sockets = (&srv.sockets)
|
||||
let mut sockets = (srv.sockets)
|
||||
.iter()
|
||||
.enumerate()
|
||||
.cycle()
|
||||
@@ -329,19 +331,23 @@ impl HostPathDiscoveryEndpoint {
|
||||
}
|
||||
}
|
||||
|
||||
error!("Unable to send message: All sockets returned errors.");
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
bail!("Unable to send message: All sockets returned errors.")
|
||||
}
|
||||
}
|
||||
|
||||
impl AppServer {
|
||||
pub fn new(sk: SSk, pk: SPk, addrs: Vec<SocketAddr>, verbosity: Verbosity) -> Result<Self> {
|
||||
pub fn new(
|
||||
sk: SSk,
|
||||
pk: SPk,
|
||||
addrs: Vec<SocketAddr>,
|
||||
verbosity: Verbosity,
|
||||
) -> anyhow::Result<Self> {
|
||||
// setup mio
|
||||
let mio_poll = mio::Poll::new()?;
|
||||
let events = mio::Events::with_capacity(8);
|
||||
|
||||
// bind each SocketAddr to a socket
|
||||
let maybe_sockets: std::result::Result<Vec<_>, std::io::Error> =
|
||||
let maybe_sockets: Result<Vec<_>, _> =
|
||||
addrs.into_iter().map(mio::net::UdpSocket::bind).collect();
|
||||
let mut sockets = maybe_sockets?;
|
||||
|
||||
@@ -408,8 +414,7 @@ impl AppServer {
|
||||
}
|
||||
|
||||
if sockets.is_empty() {
|
||||
error!("No sockets to listen on!");
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
bail!("No sockets to listen on!")
|
||||
}
|
||||
|
||||
// register all sockets to mio
|
||||
@@ -443,7 +448,7 @@ impl AppServer {
|
||||
outfile: Option<PathBuf>,
|
||||
outwg: Option<WireguardOut>,
|
||||
hostname: Option<String>,
|
||||
) -> Result<AppPeerPtr> {
|
||||
) -> anyhow::Result<AppPeerPtr> {
|
||||
let PeerPtr(pn) = self.crypt.add_peer(psk, pk)?;
|
||||
assert!(pn == self.peers.len());
|
||||
let initial_endpoint = hostname
|
||||
@@ -459,7 +464,7 @@ impl AppServer {
|
||||
Ok(AppPeerPtr(pn))
|
||||
}
|
||||
|
||||
pub fn listen_loop(&mut self) -> Result<()> {
|
||||
pub fn listen_loop(&mut self) -> anyhow::Result<()> {
|
||||
const INIT_SLEEP: f64 = 0.01;
|
||||
const MAX_FAILURES: i32 = 10;
|
||||
let mut failure_cnt = 0;
|
||||
@@ -480,11 +485,10 @@ impl AppServer {
|
||||
let sleep = INIT_SLEEP * 2.0f64.powf(f64::from(failure_cnt - 1));
|
||||
let tries_left = MAX_FAILURES - (failure_cnt - 1);
|
||||
error!(
|
||||
"unexpected error after processing {} messages: {:?}",
|
||||
"unexpected error after processing {} messages: {:?} {}",
|
||||
msgs_processed,
|
||||
err,
|
||||
// TODO do we need backtraces?
|
||||
// err.backtrace()
|
||||
err.backtrace()
|
||||
);
|
||||
if tries_left > 0 {
|
||||
error!("re-initializing networking in {sleep}! {tries_left} tries left.");
|
||||
@@ -492,12 +496,11 @@ impl AppServer {
|
||||
continue;
|
||||
}
|
||||
|
||||
error!("too many network failures");
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
bail!("too many network failures");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_loop(&mut self) -> Result<()> {
|
||||
pub fn event_loop(&mut self) -> anyhow::Result<()> {
|
||||
let (mut rx, mut tx) = (MsgBuf::zero(), MsgBuf::zero());
|
||||
|
||||
/// if socket address for peer is known, call closure
|
||||
@@ -522,9 +525,11 @@ impl AppServer {
|
||||
use AppPollResult::*;
|
||||
use KeyOutputReason::*;
|
||||
match self.poll(&mut *rx)? {
|
||||
#[allow(clippy::redundant_closure_call)]
|
||||
SendInitiation(peer) => tx_maybe_with!(peer, || self
|
||||
.crypt
|
||||
.initiate_handshake(peer.lower(), &mut *tx))?,
|
||||
#[allow(clippy::redundant_closure_call)]
|
||||
SendRetransmission(peer) => tx_maybe_with!(peer, || self
|
||||
.crypt
|
||||
.retransmit_handshake(peer.lower(), &mut *tx))?,
|
||||
@@ -546,9 +551,11 @@ impl AppServer {
|
||||
match self.crypt.handle_msg(&rx[..len], &mut *tx) {
|
||||
Err(ref e) => {
|
||||
self.verbose().then(|| {
|
||||
error!(
|
||||
"error processing incoming message from {:?}: {:?}",
|
||||
endpoint, e
|
||||
info!(
|
||||
"error processing incoming message from {:?}: {:?} {}",
|
||||
endpoint,
|
||||
e,
|
||||
e.backtrace()
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -576,7 +583,12 @@ impl AppServer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_key(&self, peer: AppPeerPtr, why: KeyOutputReason, key: &SymKey) -> Result<()> {
|
||||
pub fn output_key(
|
||||
&self,
|
||||
peer: AppPeerPtr,
|
||||
why: KeyOutputReason,
|
||||
key: &SymKey,
|
||||
) -> anyhow::Result<()> {
|
||||
let peerid = peer.lower().get(&self.crypt).pidt()?;
|
||||
let ap = peer.get_app(self);
|
||||
|
||||
@@ -611,7 +623,7 @@ impl AppServer {
|
||||
}
|
||||
|
||||
if let Some(owg) = ap.outwg.as_ref() {
|
||||
let child = Command::new("wg")
|
||||
let mut child = Command::new("wg")
|
||||
.arg("set")
|
||||
.arg(&owg.dev)
|
||||
.arg("peer")
|
||||
@@ -621,13 +633,27 @@ impl AppServer {
|
||||
.stdin(Stdio::piped())
|
||||
.args(&owg.extra_params)
|
||||
.spawn()?;
|
||||
b64_writer(child.stdin.unwrap()).write_all(key.secret())?;
|
||||
b64_writer(child.stdin.take().unwrap()).write_all(key.secret())?;
|
||||
|
||||
thread::spawn(move || {
|
||||
let status = child.wait();
|
||||
|
||||
if let Ok(status) = status {
|
||||
if status.success() {
|
||||
debug!("successfully passed psk to wg")
|
||||
} else {
|
||||
error!("could not pass psk to wg {:?}", status)
|
||||
}
|
||||
} else {
|
||||
error!("wait failed: {:?}", status)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn poll(&mut self, rx_buf: &mut [u8]) -> Result<AppPollResult> {
|
||||
pub fn poll(&mut self, rx_buf: &mut [u8]) -> anyhow::Result<AppPollResult> {
|
||||
use crate::protocol::PollResult as C;
|
||||
use AppPollResult as A;
|
||||
loop {
|
||||
@@ -652,7 +678,7 @@ impl AppServer {
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
timeout: Timing,
|
||||
) -> Result<Option<(usize, Endpoint)>> {
|
||||
) -> anyhow::Result<Option<(usize, Endpoint)>> {
|
||||
let timeout = Duration::from_secs_f64(timeout);
|
||||
|
||||
// if there is no time to wait on IO, well, then, lets not waste any time!
|
||||
@@ -1,3 +1,7 @@
|
||||
use anyhow::{bail, ensure};
|
||||
use clap::Parser;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::app_server;
|
||||
use crate::app_server::AppServer;
|
||||
use crate::util::{LoadValue, LoadValueB64};
|
||||
@@ -6,12 +10,7 @@ use crate::{
|
||||
coloring::Secret,
|
||||
pqkem::{StaticKEM, KEM},
|
||||
protocol::{SPk, SSk, SymKey},
|
||||
Result,
|
||||
RosenpassError,
|
||||
};
|
||||
use clap::Parser;
|
||||
use log::error;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use super::config;
|
||||
|
||||
@@ -99,21 +98,25 @@ pub enum Cli {
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn run() -> Result<()> {
|
||||
pub fn run() -> anyhow::Result<()> {
|
||||
let cli = Self::parse();
|
||||
|
||||
use Cli::*;
|
||||
match cli {
|
||||
Man => {
|
||||
let _man_cmd = std::process::Command::new("man")
|
||||
let man_cmd = std::process::Command::new("man")
|
||||
.args(["1", "rosenpass"])
|
||||
.status();
|
||||
|
||||
if !(man_cmd.is_ok() && man_cmd.unwrap().success()) {
|
||||
println!(include_str!(env!("ROSENPASS_MAN")));
|
||||
}
|
||||
}
|
||||
GenConfig { config_file, force } => {
|
||||
if !force && config_file.exists() {
|
||||
error!("config file {config_file:?} already exists");
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
}
|
||||
ensure!(
|
||||
force || !config_file.exists(),
|
||||
"config file {config_file:?} already exists"
|
||||
);
|
||||
|
||||
config::Rosenpass::example_config().store(config_file)?;
|
||||
}
|
||||
@@ -127,52 +130,51 @@ impl Cli {
|
||||
// figure out where the key file is specified, in the config file or directly as flag?
|
||||
let (pkf, skf) = match (config_file, public_key, secret_key) {
|
||||
(Some(config_file), _, _) => {
|
||||
if !config_file.exists() {
|
||||
error!("config file {config_file:?} does not exist");
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
}
|
||||
ensure!(
|
||||
config_file.exists(),
|
||||
"config file {config_file:?} does not exist"
|
||||
);
|
||||
|
||||
let config = config::Rosenpass::load(config_file)?;
|
||||
|
||||
(config.public_key, config.secret_key)
|
||||
}
|
||||
(_, Some(pkf), Some(skf)) => (pkf, skf),
|
||||
_ => return Err(RosenpassError::ConfigError(
|
||||
"either a config-file or both public-key and secret-key file are required"
|
||||
.into(),
|
||||
)),
|
||||
_ => {
|
||||
bail!("either a config-file or both public-key and secret-key file are required")
|
||||
}
|
||||
};
|
||||
|
||||
// check that we are not overriding something unintentionally
|
||||
let mut problems = false;
|
||||
let mut problems = vec![];
|
||||
if !force && pkf.is_file() {
|
||||
problems = true;
|
||||
error!("public-key file {pkf:?} exist, refusing to overwrite it");
|
||||
problems.push(format!(
|
||||
"public-key file {pkf:?} exist, refusing to overwrite it"
|
||||
));
|
||||
}
|
||||
if !force && skf.is_file() {
|
||||
problems = true;
|
||||
error!("secret-key file {skf:?} exist, refusing to overwrite it");
|
||||
problems.push(format!(
|
||||
"secret-key file {skf:?} exist, refusing to overwrite it"
|
||||
));
|
||||
}
|
||||
if problems {
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
if !problems.is_empty() {
|
||||
bail!(problems.join("\n"));
|
||||
}
|
||||
|
||||
// generate the keys and store them in files
|
||||
let mut ssk = crate::protocol::SSk::random();
|
||||
let mut spk = crate::protocol::SPk::random();
|
||||
StaticKEM::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
||||
|
||||
unsafe {
|
||||
StaticKEM::keygen(ssk.secret_mut(), spk.secret_mut())?;
|
||||
ssk.store_secret(skf)?;
|
||||
spk.store_secret(pkf)?;
|
||||
}
|
||||
ssk.store_secret(skf)?;
|
||||
spk.store_secret(pkf)?;
|
||||
}
|
||||
|
||||
ExchangeConfig { config_file } => {
|
||||
if !config_file.exists() {
|
||||
error!("config file '{config_file:?}' does not exist");
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
}
|
||||
ensure!(
|
||||
config_file.exists(),
|
||||
"config file '{config_file:?}' does not exist"
|
||||
);
|
||||
|
||||
let config = config::Rosenpass::load(config_file)?;
|
||||
config.validate()?;
|
||||
@@ -215,7 +217,7 @@ impl Cli {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn event_loop(config: config::Rosenpass) -> Result<()> {
|
||||
fn event_loop(config: config::Rosenpass) -> anyhow::Result<()> {
|
||||
// load own keys
|
||||
let sk = SSk::load(&config.secret_key)?;
|
||||
let pk = SPk::load(&config.public_key)?;
|
||||
@@ -248,11 +250,11 @@ impl Cli {
|
||||
}
|
||||
|
||||
trait StoreSecret {
|
||||
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()>;
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()>;
|
||||
}
|
||||
|
||||
impl<const N: usize> StoreSecret for Secret<N> {
|
||||
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> anyhow::Result<()> {
|
||||
std::fs::write(path, self.secret())?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -6,10 +6,10 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use log::{error, warn};
|
||||
use anyhow::{bail, ensure};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{util::fopen_w, Result, RosenpassError};
|
||||
use crate::util::fopen_w;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Rosenpass {
|
||||
@@ -55,6 +55,8 @@ pub struct RosenpassPeer {
|
||||
pub struct WireGuard {
|
||||
pub device: String,
|
||||
pub peer: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub extra_params: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -62,7 +64,7 @@ impl Rosenpass {
|
||||
/// Load a config file from a file path
|
||||
///
|
||||
/// no validation is conducted
|
||||
pub fn load<P: AsRef<Path>>(p: P) -> Result<Self> {
|
||||
pub fn load<P: AsRef<Path>>(p: P) -> anyhow::Result<Self> {
|
||||
let mut config: Self = toml::from_str(&fs::read_to_string(&p)?)?;
|
||||
|
||||
config.config_file_path = p.as_ref().to_owned();
|
||||
@@ -70,7 +72,7 @@ impl Rosenpass {
|
||||
}
|
||||
|
||||
/// Write a config to a file
|
||||
pub fn store<P: AsRef<Path>>(&self, p: P) -> Result<()> {
|
||||
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");
|
||||
fs::write(p, serialized_config)?;
|
||||
@@ -78,7 +80,7 @@ impl Rosenpass {
|
||||
}
|
||||
|
||||
/// Commit the configuration to where it came from, overwriting the original file
|
||||
pub fn commit(&self) -> Result<()> {
|
||||
pub fn commit(&self) -> anyhow::Result<()> {
|
||||
let mut f = fopen_w(&self.config_file_path)?;
|
||||
f.write_all(toml::to_string_pretty(&self)?.as_bytes())?;
|
||||
|
||||
@@ -86,40 +88,36 @@ impl Rosenpass {
|
||||
}
|
||||
|
||||
/// Validate a configuration
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
pub fn validate(&self) -> anyhow::Result<()> {
|
||||
// check the public-key file exists
|
||||
if !(self.public_key.is_file()) {
|
||||
return Err(RosenpassError::ConfigError(format!(
|
||||
"public-key file {:?} does not exist",
|
||||
self.public_key
|
||||
)));
|
||||
}
|
||||
ensure!(
|
||||
self.public_key.is_file(),
|
||||
"public-key file {:?} does not exist",
|
||||
self.public_key
|
||||
);
|
||||
|
||||
// check the secret-key file exists
|
||||
if !(self.secret_key.is_file()) {
|
||||
return Err(RosenpassError::ConfigError(format!(
|
||||
"secret-key file {:?} does not exist",
|
||||
self.secret_key
|
||||
)));
|
||||
}
|
||||
ensure!(
|
||||
self.secret_key.is_file(),
|
||||
"secret-key file {:?} does not exist",
|
||||
self.secret_key
|
||||
);
|
||||
|
||||
for (i, peer) in self.peers.iter().enumerate() {
|
||||
// check peer's public-key file exists
|
||||
if !(peer.public_key.is_file()) {
|
||||
return Err(RosenpassError::ConfigError(format!(
|
||||
"peer {i} public-key file {:?} does not exist",
|
||||
peer.public_key
|
||||
)));
|
||||
}
|
||||
ensure!(
|
||||
peer.public_key.is_file(),
|
||||
"peer {i} public-key file {:?} does not exist",
|
||||
peer.public_key
|
||||
);
|
||||
|
||||
// check endpoint is usable
|
||||
if let Some(addr) = peer.endpoint.as_ref() {
|
||||
if !(addr.to_socket_addrs().is_ok()) {
|
||||
return Err(RosenpassError::ConfigError(format!(
|
||||
"peer {i} endpoint {} can not be parsed to a socket address",
|
||||
addr
|
||||
)));
|
||||
}
|
||||
ensure!(
|
||||
addr.to_socket_addrs().is_ok(),
|
||||
"peer {i} endpoint {} can not be parsed to a socket address",
|
||||
addr
|
||||
);
|
||||
}
|
||||
|
||||
// TODO warn if neither out_key nor exchange_command is defined
|
||||
@@ -155,7 +153,7 @@ impl Rosenpass {
|
||||
|
||||
/// from chaotic args
|
||||
/// Quest: the grammar is undecideable, what do we do here?
|
||||
pub fn parse_args(args: Vec<String>) -> Result<Self> {
|
||||
pub fn parse_args(args: Vec<String>) -> anyhow::Result<Self> {
|
||||
let mut config = Self::new("", "");
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
@@ -178,7 +176,6 @@ impl Rosenpass {
|
||||
|
||||
// TODO idea: use config.peers.len() to give index of peer with conflicting argument
|
||||
use State::*;
|
||||
let mut problem = false;
|
||||
let mut state = Own;
|
||||
let mut current_peer = None;
|
||||
let p_exists = "a peer should exist by now";
|
||||
@@ -188,7 +185,9 @@ impl Rosenpass {
|
||||
(Own, "public-key", None) => OwnPublicKey,
|
||||
(Own, "secret-key", None) => OwnSecretKey,
|
||||
(Own, "private-key", None) => {
|
||||
warn!("the private-key argument is deprecated, please use secret-key instead");
|
||||
log::warn!(
|
||||
"the private-key argument is deprecated, please use secret-key instead"
|
||||
);
|
||||
OwnSecretKey
|
||||
}
|
||||
(Own, "listen", None) => OwnListen,
|
||||
@@ -197,16 +196,14 @@ impl Rosenpass {
|
||||
Own
|
||||
}
|
||||
(Own, "peer", None) => {
|
||||
if !(already_set.contains(&OwnPublicKey)) {
|
||||
return Err(RosenpassError::ConfigError(
|
||||
"public-key file must be set".into(),
|
||||
));
|
||||
}
|
||||
if !(already_set.contains(&OwnSecretKey)) {
|
||||
return Err(RosenpassError::ConfigError(
|
||||
"secret-key file must be set".into(),
|
||||
));
|
||||
}
|
||||
ensure!(
|
||||
already_set.contains(&OwnPublicKey),
|
||||
"public-key file must be set"
|
||||
);
|
||||
ensure!(
|
||||
already_set.contains(&OwnSecretKey),
|
||||
"secret-key file must be set"
|
||||
);
|
||||
|
||||
already_set.clear();
|
||||
current_peer = Some(RosenpassPeer::default());
|
||||
@@ -214,20 +211,18 @@ impl Rosenpass {
|
||||
Peer
|
||||
}
|
||||
(OwnPublicKey, pk, None) => {
|
||||
if !(already_set.insert(OwnPublicKey)) {
|
||||
return Err(RosenpassError::ConfigError(
|
||||
"public-key was already set".into(),
|
||||
));
|
||||
}
|
||||
ensure!(
|
||||
already_set.insert(OwnPublicKey),
|
||||
"public-key was already set"
|
||||
);
|
||||
config.public_key = pk.into();
|
||||
Own
|
||||
}
|
||||
(OwnSecretKey, sk, None) => {
|
||||
if !(already_set.insert(OwnSecretKey)) {
|
||||
return Err(RosenpassError::ConfigError(
|
||||
"secret-key was already set".into(),
|
||||
));
|
||||
}
|
||||
ensure!(
|
||||
already_set.insert(OwnSecretKey),
|
||||
"secret-key was already set"
|
||||
);
|
||||
config.secret_key = sk.into();
|
||||
Own
|
||||
}
|
||||
@@ -255,45 +250,36 @@ impl Rosenpass {
|
||||
(Peer, "outfile", Some(_)) => PeerOutfile,
|
||||
(Peer, "wireguard", Some(_)) => PeerWireguardDev,
|
||||
(PeerPublicKey, pk, Some(peer)) => {
|
||||
if !(already_set.insert(PeerPublicKey)) {
|
||||
return Err(RosenpassError::ConfigError(
|
||||
"public-key was already set".into(),
|
||||
));
|
||||
}
|
||||
ensure!(
|
||||
already_set.insert(PeerPublicKey),
|
||||
"public-key was already set"
|
||||
);
|
||||
peer.public_key = pk.into();
|
||||
Peer
|
||||
}
|
||||
(PeerEndpoint, e, Some(peer)) => {
|
||||
if !already_set.insert(PeerEndpoint) {
|
||||
error!("endpoint was already set");
|
||||
problem = true;
|
||||
}
|
||||
ensure!(already_set.insert(PeerEndpoint), "endpoint was already set");
|
||||
peer.endpoint = Some(e.to_owned());
|
||||
Peer
|
||||
}
|
||||
(PeerPsk, psk, Some(peer)) => {
|
||||
if !already_set.insert(PeerEndpoint) {
|
||||
error!("peer psk was already set");
|
||||
problem = true;
|
||||
}
|
||||
ensure!(already_set.insert(PeerEndpoint), "peer psk was already set");
|
||||
peer.pre_shared_key = Some(psk.into());
|
||||
Peer
|
||||
}
|
||||
(PeerOutfile, of, Some(peer)) => {
|
||||
if !(already_set.insert(PeerOutfile)) {
|
||||
return Err(RosenpassError::ConfigError(
|
||||
"peer outfile was already set".into(),
|
||||
));
|
||||
}
|
||||
ensure!(
|
||||
already_set.insert(PeerOutfile),
|
||||
"peer outfile was already set"
|
||||
);
|
||||
peer.key_out = Some(of.into());
|
||||
Peer
|
||||
}
|
||||
(PeerWireguardDev, dev, Some(peer)) => {
|
||||
if !(already_set.insert(PeerWireguardDev)) {
|
||||
return Err(RosenpassError::ConfigError(
|
||||
"peer wireguard-dev was already set".into(),
|
||||
));
|
||||
}
|
||||
ensure!(
|
||||
already_set.insert(PeerWireguardDev),
|
||||
"peer wireguard-dev was already set"
|
||||
);
|
||||
assert!(peer.wg.is_none());
|
||||
peer.wg = Some(WireGuard {
|
||||
device: dev.to_string(),
|
||||
@@ -303,11 +289,10 @@ impl Rosenpass {
|
||||
PeerWireguardPeer
|
||||
}
|
||||
(PeerWireguardPeer, p, Some(peer)) => {
|
||||
if !(already_set.insert(PeerWireguardPeer)) {
|
||||
return Err(RosenpassError::ConfigError(
|
||||
"peer wireguard-peer was already set".into(),
|
||||
));
|
||||
}
|
||||
ensure!(
|
||||
already_set.insert(PeerWireguardPeer),
|
||||
"peer wireguard-peer was already set"
|
||||
);
|
||||
peer.wg.as_mut().expect(wg_exists).peer = p.to_string();
|
||||
PeerWireguardExtraArgs
|
||||
}
|
||||
@@ -322,16 +307,14 @@ impl Rosenpass {
|
||||
|
||||
// error cases
|
||||
(Own, x, None) => {
|
||||
error!("unrecognised argument {x}");
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
bail!("unrecognised argument {x}");
|
||||
}
|
||||
(Own | OwnPublicKey | OwnSecretKey | OwnListen, _, Some(_)) => {
|
||||
panic!("current_peer is not None while in Own* state, this must never happen")
|
||||
}
|
||||
|
||||
(State::Peer, arg, Some(_)) => {
|
||||
error!("unrecongnised argument {arg}");
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
bail!("unrecongnised argument {arg}");
|
||||
}
|
||||
(
|
||||
Peer
|
||||
@@ -350,10 +333,6 @@ impl Rosenpass {
|
||||
};
|
||||
}
|
||||
|
||||
if problem {
|
||||
return Err(RosenpassError::RuntimeError);
|
||||
}
|
||||
|
||||
if let Some(p) = current_peer {
|
||||
// TODO ensure peer is propagated with sufficient information
|
||||
config.peers.push(p);
|
||||
@@ -2,8 +2,8 @@
|
||||
//! ensures their uniqueness
|
||||
|
||||
use {
|
||||
crate::Result,
|
||||
crate::{prftree::PrfTree, sodium::KEY_SIZE},
|
||||
anyhow::Result,
|
||||
};
|
||||
|
||||
pub fn protocol() -> Result<PrfTree> {
|
||||
60
rosenpass/src/lib.rs
Normal file
60
rosenpass/src/lib.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
#[macro_use]
|
||||
pub mod util;
|
||||
#[macro_use]
|
||||
pub mod sodium;
|
||||
pub mod coloring;
|
||||
#[rustfmt::skip]
|
||||
pub mod labeled_prf;
|
||||
pub mod app_server;
|
||||
pub mod cli;
|
||||
pub mod config;
|
||||
pub mod msgs;
|
||||
pub mod pqkem;
|
||||
pub mod prftree;
|
||||
pub mod protocol;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum RosenpassError {
|
||||
#[error("error in OQS")]
|
||||
Oqs,
|
||||
#[error("error from external library while calling OQS")]
|
||||
OqsExternalLib,
|
||||
#[error("buffer size mismatch, required {required_size} but found {actual_size}")]
|
||||
BufferSizeMismatch {
|
||||
required_size: usize,
|
||||
actual_size: usize,
|
||||
},
|
||||
#[error("invalid message type")]
|
||||
InvalidMessageType(u8),
|
||||
}
|
||||
|
||||
impl RosenpassError {
|
||||
/// Helper function to check a buffer size
|
||||
fn check_buffer_size(required_size: usize, actual_size: usize) -> Result<(), Self> {
|
||||
if required_size != actual_size {
|
||||
Err(Self::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait to attach function calls to foreign types.
|
||||
trait RosenpassMaybeError {
|
||||
/// Checks whether something is an error or not
|
||||
fn to_rg_error(&self) -> Result<(), RosenpassError>;
|
||||
}
|
||||
|
||||
impl RosenpassMaybeError for oqs_sys::common::OQS_STATUS {
|
||||
fn to_rg_error(&self) -> Result<(), RosenpassError> {
|
||||
use oqs_sys::common::OQS_STATUS;
|
||||
match self {
|
||||
OQS_STATUS::OQS_SUCCESS => Ok(()),
|
||||
OQS_STATUS::OQS_ERROR => Err(RosenpassError::Oqs),
|
||||
OQS_STATUS::OQS_EXTERNAL_LIB_ERROR_OPENSSL => Err(RosenpassError::OqsExternalLib),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
//! This is a generalization of a PRF operating
|
||||
//! on a sequence of inputs instead of a single input.
|
||||
//!
|
||||
//! Like a Dec function the Iprf features efficient
|
||||
//! Like a Dec function the Iprf features efficient
|
||||
//! incrementability.
|
||||
//!
|
||||
//! You can also think of an Iprf as a Dec function with
|
||||
@@ -27,7 +27,7 @@ pub fn prf_into(out: &mut [u8], key: &[u8], data: &[u8]) {
|
||||
hmac_into(out, key, data).unwrap()
|
||||
}
|
||||
|
||||
pub fn prf(key: &[u8], data: &[u8]) -> [u8; KEY_SIZE]{
|
||||
pub fn prf(key: &[u8], data: &[u8]) -> [u8; KEY_SIZE] {
|
||||
mutating([0u8; KEY_SIZE], |r| prf_into(r, key, data))
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ impl Iprf {
|
||||
IprfBranch(self.0)
|
||||
}
|
||||
|
||||
// TODO: Protocol! Use domain separation to ensure that
|
||||
// TODO: Protocol! Use domain separation to ensure that
|
||||
fn mix(self, v: &[u8]) -> Self {
|
||||
Self(prf(&self.0, v))
|
||||
}
|
||||
|
||||
|
||||
fn mix_secret<const N: usize>(self, v: Secret<N>) -> SecretIprf {
|
||||
SecretIprf::prf_invoc(&self.0, v.secret())
|
||||
}
|
||||
@@ -70,8 +70,9 @@ impl IprfBranch {
|
||||
|
||||
impl SecretIprf {
|
||||
fn prf_invoc(k: &[u8], d: &[u8]) -> SecretIprf {
|
||||
mutating(SecretIprf(Secret::zero()), |r|
|
||||
prf_into(k, d, r.secret_mut()))
|
||||
mutating(SecretIprf(Secret::zero()), |r| {
|
||||
prf_into(k, d, r.secret_mut())
|
||||
})
|
||||
}
|
||||
|
||||
fn from_key(k: Secret<N>) -> SecretIprf {
|
||||
@@ -143,7 +143,7 @@ macro_rules! data_lense(
|
||||
pub fn check_size(len: usize) -> Result<(), RosenpassError>{
|
||||
let required_size = $( $len + )+ 0;
|
||||
let actual_size = len;
|
||||
if required_size < actual_size {
|
||||
if required_size != actual_size {
|
||||
Err(RosenpassError::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
@@ -199,23 +199,53 @@ macro_rules! data_lense(
|
||||
type __ContainerType;
|
||||
|
||||
/// Create a lense to the byte slice
|
||||
fn [< $type:snake >] $(< $($generic),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError>;
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError>;
|
||||
|
||||
/// Create a lense to the byte slice, automatically truncating oversized buffers
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError>;
|
||||
}
|
||||
|
||||
impl<'a> [< $type Ext >] for &'a [u8] {
|
||||
type __ContainerType = &'a [u8];
|
||||
|
||||
fn [< $type:snake >] $(< $($generic),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
$type::<Self::__ContainerType, $( $($generic),+ )? >::check_size(self.len())?;
|
||||
Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? ))
|
||||
}
|
||||
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
let required_size = $( $len + )+ 0;
|
||||
let actual_size = self.len();
|
||||
if actual_size < required_size {
|
||||
return Err(RosenpassError::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
});
|
||||
}
|
||||
|
||||
[< $type Ext >]::[< $type:snake >](&self[..required_size])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> [< $type Ext >] for &'a mut [u8] {
|
||||
type __ContainerType = &'a mut [u8];
|
||||
|
||||
fn [< $type:snake >] $(< $($generic),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
fn [< $type:snake >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
$type::<Self::__ContainerType, $( $($generic),+ )? >::check_size(self.len())?;
|
||||
Ok($type ( self, $( $( ::core::marker::PhantomData::<$generic> ),+ )? ))
|
||||
}
|
||||
|
||||
fn [< $type:snake _ truncating >] $(< $($generic : LenseView),* >)? (self) -> Result< $type<Self::__ContainerType, $( $($generic),+ )? >, RosenpassError> {
|
||||
let required_size = $( $len + )+ 0;
|
||||
let actual_size = self.len();
|
||||
if actual_size < required_size {
|
||||
return Err(RosenpassError::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
});
|
||||
}
|
||||
|
||||
[< $type Ext >]::[< $type:snake >](&mut self[..required_size])
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
@@ -1,8 +1,10 @@
|
||||
//! Implementation of the tree-like structure used for the label derivation in [labeled_prf](crate::labeled_prf)
|
||||
use crate::{
|
||||
coloring::Secret,
|
||||
sodium::{hmac, hmac_into, KEY_SIZE},
|
||||
Result,
|
||||
use {
|
||||
crate::{
|
||||
coloring::Secret,
|
||||
sodium::{hmac, hmac_into, KEY_SIZE},
|
||||
},
|
||||
anyhow::Result,
|
||||
};
|
||||
|
||||
// TODO Use a proper Dec interface
|
||||
@@ -23,10 +23,10 @@
|
||||
//! pqkem::{StaticKEM, KEM},
|
||||
//! protocol::{SSk, SPk, MsgBuf, PeerPtr, CryptoServer, SymKey},
|
||||
//! };
|
||||
//! # fn main() -> Result<(), rosenpass::RosenpassError> {
|
||||
//! # fn main() -> anyhow::Result<()> {
|
||||
//!
|
||||
//! // always init libsodium before anything
|
||||
//! rosenpass::sodium::sodium_init().unwrap();
|
||||
//! rosenpass::sodium::sodium_init()?;
|
||||
//!
|
||||
//! // initialize secret and public key for peer a ...
|
||||
//! let (mut peer_a_sk, mut peer_a_pk) = (SSk::zero(), SPk::zero());
|
||||
@@ -42,32 +42,31 @@
|
||||
//! 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).unwrap();
|
||||
//! b.add_peer(Some(psk), peer_a_pk).unwrap();
|
||||
//! a.add_peer(Some(psk.clone()), peer_b_pk)?;
|
||||
//! b.add_peer(Some(psk), peer_a_pk)?;
|
||||
//!
|
||||
//! // declare buffers for message exchange
|
||||
//! let (mut a_buf, mut b_buf) = (MsgBuf::zero(), MsgBuf::zero());
|
||||
//!
|
||||
//! // let a initiate a handshake
|
||||
//! let length = a.initiate_handshake(PeerPtr(0), a_buf.as_mut_slice());
|
||||
//! let mut maybe_len = Some(a.initiate_handshake(PeerPtr(0), a_buf.as_mut_slice())?);
|
||||
//!
|
||||
//! // let b respond to a and a respond to b, in two rounds
|
||||
//! for _ in 0..2 {
|
||||
//! b.handle_msg(&a_buf[..], &mut b_buf[..]);
|
||||
//! a.handle_msg(&b_buf[..], &mut a_buf[..]);
|
||||
//! // 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.unwrap().secret(), b_key.unwrap().secret(),
|
||||
//! 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(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use log::{trace, warn};
|
||||
|
||||
use crate::{
|
||||
coloring::*,
|
||||
labeled_prf as lprf,
|
||||
@@ -76,8 +75,8 @@ use crate::{
|
||||
prftree::{SecretPrfTree, SecretPrfTreeBranch},
|
||||
sodium::*,
|
||||
util::*,
|
||||
Result, RosenpassError,
|
||||
};
|
||||
use anyhow::{bail, ensure, Context, Result};
|
||||
use std::collections::hash_map::{
|
||||
Entry::{Occupied, Vacant},
|
||||
HashMap,
|
||||
@@ -487,8 +486,10 @@ impl CryptoServer {
|
||||
let peerid = peer.pidt()?;
|
||||
let peerno = self.peers.len();
|
||||
match self.index.entry(IndexKey::Peer(peerid)) {
|
||||
// TODO improve type handling, use PeerPtr
|
||||
Occupied(_) => return Err(RosenpassError::PeerIdAlreadyTaken(peerid)),
|
||||
Occupied(_) => bail!(
|
||||
"Cannot insert peer with id {:?}; peer with this id already registered.",
|
||||
peerid
|
||||
),
|
||||
Vacant(e) => e.insert(peerno),
|
||||
};
|
||||
self.peers.push(peer);
|
||||
@@ -500,7 +501,7 @@ impl CryptoServer {
|
||||
pub fn register_session(&mut self, id: SessionId, peer: PeerPtr) -> Result<()> {
|
||||
match self.index.entry(IndexKey::Sid(id)) {
|
||||
Occupied(p) if PeerPtr(*p.get()) == peer => {} // Already registered
|
||||
Occupied(_) => return Err(RosenpassError::SessionIdAlreadyTaken(id)),
|
||||
Occupied(_) => bail!("Cannot insert session with id {:?}; id is in use.", id),
|
||||
Vacant(e) => {
|
||||
e.insert(peer.0);
|
||||
}
|
||||
@@ -522,11 +523,8 @@ impl CryptoServer {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn find_peer(&self, id: PeerId) -> Result<PeerPtr> {
|
||||
self.index
|
||||
.get(&IndexKey::Peer(id))
|
||||
.map(|no| PeerPtr(*no))
|
||||
.ok_or_else(|| RosenpassError::NoSuchPeerId(id))
|
||||
pub fn find_peer(&self, id: PeerId) -> Option<PeerPtr> {
|
||||
self.index.get(&IndexKey::Peer(id)).map(|no| PeerPtr(*no))
|
||||
}
|
||||
|
||||
// lookup_session in whitepaper
|
||||
@@ -739,7 +737,7 @@ impl CryptoServer {
|
||||
// TODO remove unnecessary copying between global tx_buf and per-peer buf
|
||||
// TODO move retransmission storage to io server
|
||||
pub fn initiate_handshake(&mut self, peer: PeerPtr, tx_buf: &mut [u8]) -> Result<usize> {
|
||||
let mut msg = tx_buf.envelope::<InitHello<()>>()?; // Envelope::<InitHello>::default(); // TODO
|
||||
let mut msg = tx_buf.envelope_truncating::<InitHello<()>>()?; // Envelope::<InitHello>::default(); // TODO
|
||||
self.handle_initiation(peer, msg.payload_mut().init_hello()?)?;
|
||||
let len = self.seal_and_commit_msg(peer, MsgType::InitHello, msg)?;
|
||||
peer.hs()
|
||||
@@ -784,23 +782,19 @@ impl CryptoServer {
|
||||
/// | t2 | `InitConf` | -> | |
|
||||
/// | t3 | | <- | `EmptyData` |
|
||||
pub fn handle_msg(&mut self, rx_buf: &[u8], tx_buf: &mut [u8]) -> Result<HandleMsgResult> {
|
||||
let seal_broken = "Message seal broken!";
|
||||
// length of the response. We assume no response, so None for now
|
||||
let mut len = 0;
|
||||
let mut exchanged = false;
|
||||
|
||||
if rx_buf.is_empty() {
|
||||
trace!("received empty message, ignoring it");
|
||||
return Err(RosenpassError::EmptyMessage);
|
||||
}
|
||||
ensure!(!rx_buf.is_empty(), "received empty message, ignoring it");
|
||||
|
||||
let peer = match rx_buf[0].try_into() {
|
||||
Ok(MsgType::InitHello) => {
|
||||
let msg_in = rx_buf.envelope::<InitHello<&[u8]>>()?;
|
||||
if msg_in.check_seal(self)? {
|
||||
return Err(RosenpassError::SealBroken);
|
||||
}
|
||||
ensure!(msg_in.check_seal(self)?, seal_broken);
|
||||
|
||||
let mut msg_out = tx_buf.envelope::<RespHello<&mut [u8]>>()?;
|
||||
let mut msg_out = tx_buf.envelope_truncating::<RespHello<&mut [u8]>>()?;
|
||||
let peer = self.handle_init_hello(
|
||||
msg_in.payload().init_hello()?,
|
||||
msg_out.payload_mut().resp_hello()?,
|
||||
@@ -810,11 +804,9 @@ impl CryptoServer {
|
||||
}
|
||||
Ok(MsgType::RespHello) => {
|
||||
let msg_in = rx_buf.envelope::<RespHello<&[u8]>>()?;
|
||||
if msg_in.check_seal(self)? {
|
||||
return Err(RosenpassError::SealBroken);
|
||||
}
|
||||
ensure!(msg_in.check_seal(self)?, seal_broken);
|
||||
|
||||
let mut msg_out = tx_buf.envelope::<InitConf<&mut [u8]>>()?;
|
||||
let mut msg_out = tx_buf.envelope_truncating::<InitConf<&mut [u8]>>()?;
|
||||
let peer = self.handle_resp_hello(
|
||||
msg_in.payload().resp_hello()?,
|
||||
msg_out.payload_mut().init_conf()?,
|
||||
@@ -827,11 +819,9 @@ impl CryptoServer {
|
||||
}
|
||||
Ok(MsgType::InitConf) => {
|
||||
let msg_in = rx_buf.envelope::<InitConf<&[u8]>>()?;
|
||||
if msg_in.check_seal(self)? {
|
||||
return Err(RosenpassError::SealBroken);
|
||||
}
|
||||
ensure!(msg_in.check_seal(self)?, seal_broken);
|
||||
|
||||
let mut msg_out = tx_buf.envelope::<EmptyData<&mut [u8]>>()?;
|
||||
let mut msg_out = tx_buf.envelope_truncating::<EmptyData<&mut [u8]>>()?;
|
||||
let peer = self.handle_init_conf(
|
||||
msg_in.payload().init_conf()?,
|
||||
msg_out.payload_mut().empty_data()?,
|
||||
@@ -842,23 +832,15 @@ impl CryptoServer {
|
||||
}
|
||||
Ok(MsgType::EmptyData) => {
|
||||
let msg_in = rx_buf.envelope::<EmptyData<&[u8]>>()?;
|
||||
if msg_in.check_seal(self)? {
|
||||
return Err(RosenpassError::SealBroken);
|
||||
}
|
||||
ensure!(msg_in.check_seal(self)?, seal_broken);
|
||||
|
||||
self.handle_resp_conf(msg_in.payload().empty_data()?)?
|
||||
}
|
||||
Ok(MsgType::DataMsg) => {
|
||||
return Err(RosenpassError::NotImplemented(
|
||||
"DataMsg handling not implemented!",
|
||||
))
|
||||
Ok(MsgType::DataMsg) => bail!("DataMsg handling not implemented!"),
|
||||
Ok(MsgType::CookieReply) => bail!("CookieReply handling not implemented!"),
|
||||
Err(_) => {
|
||||
bail!("CookieReply handling not implemented!")
|
||||
}
|
||||
Ok(MsgType::CookieReply) => {
|
||||
return Err(RosenpassError::NotImplemented(
|
||||
"CookieReply handling not implemented!",
|
||||
))
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
Ok(HandleMsgResult {
|
||||
@@ -1137,10 +1119,10 @@ impl CryptoServer {
|
||||
|
||||
impl IniHsPtr {
|
||||
pub fn store_msg_for_retransmission(&self, srv: &mut CryptoServer, msg: &[u8]) -> Result<()> {
|
||||
let ih = self.get_mut(srv).as_mut().ok_or_else(|| {
|
||||
warn!("No current handshake for peer {:?}", self.peer());
|
||||
RosenpassError::NoCurrentHs(self.peer())
|
||||
})?;
|
||||
let ih = self
|
||||
.get_mut(srv)
|
||||
.as_mut()
|
||||
.with_context(|| format!("No current handshake for peer {:?}", self.peer()))?;
|
||||
cpy_min(msg, &mut *ih.tx_buf);
|
||||
ih.tx_count = 0;
|
||||
ih.tx_len = msg.len();
|
||||
@@ -1149,20 +1131,20 @@ impl IniHsPtr {
|
||||
}
|
||||
|
||||
pub fn apply_retransmission(&self, srv: &mut CryptoServer, tx_buf: &mut [u8]) -> Result<usize> {
|
||||
let ih = self.get_mut(srv).as_mut().ok_or_else(|| {
|
||||
warn!("No current handshake for peer {:?}", self.peer());
|
||||
RosenpassError::NoCurrentHs(self.peer())
|
||||
})?;
|
||||
let ih = self
|
||||
.get_mut(srv)
|
||||
.as_mut()
|
||||
.with_context(|| format!("No current handshake for peer {:?}", self.peer()))?;
|
||||
cpy_min(&ih.tx_buf[..ih.tx_len], tx_buf);
|
||||
Ok(ih.tx_len)
|
||||
}
|
||||
|
||||
pub fn register_retransmission(&self, srv: &mut CryptoServer) -> Result<()> {
|
||||
let tb = srv.timebase.clone();
|
||||
let ih = self.get_mut(srv).as_mut().ok_or_else(|| {
|
||||
warn!("No current handshake for peer {:?}", self.peer());
|
||||
RosenpassError::NoCurrentHs(self.peer())
|
||||
})?;
|
||||
let ih = self
|
||||
.get_mut(srv)
|
||||
.as_mut()
|
||||
.with_context(|| format!("No current handshake for peer {:?}", self.peer()))?;
|
||||
// Base delay, exponential increase, ±50% jitter
|
||||
ih.tx_retry_at = tb.now()
|
||||
+ RETRANSMIT_DELAY_BEGIN
|
||||
@@ -1366,16 +1348,18 @@ impl HandshakeState {
|
||||
hs.mix(biscuit_ct)?;
|
||||
|
||||
// Look up the associated peer
|
||||
let peer = srv.find_peer(pid)?;
|
||||
let peer = srv
|
||||
.find_peer(pid) // TODO: FindPeer should return a Result<()>
|
||||
.with_context(|| format!("Could not decode biscuit for peer {pid:?}: No such peer."))?;
|
||||
|
||||
// Defense against replay attacks; implementations may accept
|
||||
// the most recent biscuit no again (bn = peer.bn_{prev}) which
|
||||
// indicates retransmission
|
||||
// TODO: Handle retransmissions without involving the crypto code
|
||||
if sodium_bigint_cmp(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) >= 0 {
|
||||
warn!("Rejecting biscuit: Outdated biscuit number");
|
||||
return Err(RosenpassError::InvalidBiscuitNo);
|
||||
}
|
||||
ensure!(
|
||||
sodium_bigint_cmp(biscuit.biscuit_no(), &*peer.get(srv).biscuit_used) >= 0,
|
||||
"Rejecting biscuit: Outdated biscuit number"
|
||||
);
|
||||
|
||||
Ok((peer, no, hs))
|
||||
}
|
||||
@@ -1409,10 +1393,11 @@ impl CryptoServer {
|
||||
///
|
||||
/// Fail if no session is available with the peer
|
||||
pub fn osk(&self, peer: PeerPtr) -> Result<SymKey> {
|
||||
let session = peer.session().get(self).as_ref().ok_or_else(|| {
|
||||
warn!("No current session for peer {peer:?}");
|
||||
RosenpassError::NoSuchPeer(peer)
|
||||
})?;
|
||||
let session = peer
|
||||
.session()
|
||||
.get(self)
|
||||
.as_ref()
|
||||
.with_context(|| format!("No current session for peer {:?}", peer))?;
|
||||
Ok(session.ck.mix(&lprf::osk()?)?.into_secret())
|
||||
}
|
||||
}
|
||||
@@ -1492,7 +1477,8 @@ impl CryptoServer {
|
||||
let peer = {
|
||||
let mut peerid = PeerId::zero();
|
||||
core.decrypt_and_mix(&mut *peerid, ih.pidic())?;
|
||||
self.find_peer(peerid)?
|
||||
self.find_peer(peerid)
|
||||
.with_context(|| format!("No such peer {peerid:?}."))?
|
||||
};
|
||||
|
||||
// IHR7
|
||||
@@ -1534,12 +1520,13 @@ impl CryptoServer {
|
||||
mut ic: InitConf<&mut [u8]>,
|
||||
) -> Result<PeerPtr> {
|
||||
// RHI2
|
||||
let sid = SessionId::from_slice(rh.sidi());
|
||||
let peer = self
|
||||
.lookup_handshake(sid)
|
||||
.ok_or_else(|| {
|
||||
warn!("Got RespHello packet for non-existent session {sid:?}");
|
||||
RosenpassError::NoSuchSessionId(sid)
|
||||
.lookup_handshake(SessionId::from_slice(rh.sidi()))
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Got RespHello packet for non-existent session {:?}",
|
||||
rh.sidi()
|
||||
)
|
||||
})?
|
||||
.peer();
|
||||
|
||||
@@ -1560,14 +1547,13 @@ impl CryptoServer {
|
||||
let exp = hs!().next;
|
||||
let got = HandshakeStateMachine::RespHello;
|
||||
|
||||
if exp != got {
|
||||
warn!("Unexpected package in session {sid:?}. Expected {exp:?}, got {got:?}.",);
|
||||
return Err(RosenpassError::UnexpectedMessage {
|
||||
session: sid,
|
||||
expected: Some(exp),
|
||||
got: Some(got),
|
||||
});
|
||||
}
|
||||
ensure!(
|
||||
exp == got,
|
||||
"Unexpected package in session {:?}. Expected {:?}, got {:?}.",
|
||||
SessionId::from_slice(rh.sidi()),
|
||||
exp,
|
||||
got
|
||||
);
|
||||
|
||||
let mut core = hs!().core.clone();
|
||||
core.sidr.copy_from_slice(rh.sidr());
|
||||
@@ -1684,10 +1670,11 @@ impl CryptoServer {
|
||||
// instead of a generic PeerPtr::send(&Server, Option<&[u8]>) -> Either<EmptyData, Data>
|
||||
// because data transmission is a stub currently. This software is supposed to be used
|
||||
// as a key exchange service feeding a PSK into some classical (i.e. non post quantum)
|
||||
let ses = peer.session().get_mut(self).as_mut().ok_or_else(|| {
|
||||
warn!("Cannot send acknowledgement. No session.");
|
||||
RosenpassError::NoSession
|
||||
})?;
|
||||
let ses = peer
|
||||
.session()
|
||||
.get_mut(self)
|
||||
.as_mut()
|
||||
.context("Cannot send acknowledgement. No session.")?;
|
||||
rc.sid_mut().copy_from_slice(&ses.sidt.value);
|
||||
rc.ctr_mut().copy_from_slice(&ses.txnm.to_le_bytes());
|
||||
ses.txnm += 1; // Increment nonce before encryption, just in case an error is raised
|
||||
@@ -1701,39 +1688,30 @@ impl CryptoServer {
|
||||
|
||||
pub fn handle_resp_conf(&mut self, rc: EmptyData<&[u8]>) -> Result<PeerPtr> {
|
||||
let sid = SessionId::from_slice(rc.sid());
|
||||
let hs = self.lookup_handshake(sid).ok_or_else(|| {
|
||||
warn!("Got RespConf packet for non-existent session {sid:?}");
|
||||
RosenpassError::InvalidSessionId(sid)
|
||||
})?;
|
||||
let hs = self
|
||||
.lookup_handshake(sid)
|
||||
.with_context(|| format!("Got RespConf packet for non-existent session {sid:?}"))?;
|
||||
let ses = hs.peer().session();
|
||||
|
||||
let exp = hs.get(self).as_ref().map(|h| h.next);
|
||||
let got = Some(HandshakeStateMachine::RespConf);
|
||||
|
||||
if exp != got {
|
||||
warn!("Unexpected package in session {sid:?}. Expected {exp:?}, got {got:?}.",);
|
||||
return Err(RosenpassError::UnexpectedMessage {
|
||||
session: sid,
|
||||
expected: exp,
|
||||
got,
|
||||
});
|
||||
}
|
||||
ensure!(
|
||||
exp == got,
|
||||
"Unexpected package in session {:?}. Expected {:?}, got {:?}.",
|
||||
sid,
|
||||
exp,
|
||||
got
|
||||
);
|
||||
|
||||
// Validate the message
|
||||
{
|
||||
// TODO get rid of as_mut necessity
|
||||
let s = ses.get_mut(self).as_mut().ok_or_else(|| {
|
||||
warn!("Cannot validate EmptyData message. Missing encryption session for {sid:?}");
|
||||
RosenpassError::NoSuchSessionId(sid)
|
||||
let s = ses.get_mut(self).as_mut().with_context(|| {
|
||||
format!("Cannot validate EmptyData message. Missing encryption session for {sid:?}")
|
||||
})?;
|
||||
// the unwrap can not fail, because the slice returned by ctr() is
|
||||
// guaranteed to have the correct size
|
||||
let n = u64::from_le_bytes(rc.ctr().try_into().unwrap());
|
||||
|
||||
if n < s.txnt {
|
||||
trace!("Stale nonce");
|
||||
return Err(RosenpassError::StaleNonce);
|
||||
}
|
||||
ensure!(n >= s.txnt, "Stale nonce");
|
||||
s.txnt = n;
|
||||
aead_dec_into(
|
||||
// pt, k, n, ad, ct
|
||||
@@ -1756,27 +1734,94 @@ impl CryptoServer {
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
fn init_crypto_server() -> CryptoServer {
|
||||
// always init libsodium before anything
|
||||
#[test]
|
||||
/// Ensure that the protocol implementation can deal with truncated
|
||||
/// messages and with overlong messages.
|
||||
///
|
||||
/// This test performs a complete handshake between two randomly generated
|
||||
/// servers; instead of delivering the message correctly at first messages
|
||||
/// of length zero through about 1.2 times the correct message size are delivered.
|
||||
///
|
||||
/// Producing an error is expected on each of these messages.
|
||||
///
|
||||
/// Finally the correct message is delivered and the same process
|
||||
/// starts again in the other direction.
|
||||
///
|
||||
/// Through all this, the handshake should still successfully terminate;
|
||||
/// i.e. an exchanged key must be produced in both servers.
|
||||
fn handles_incorrect_size_messages() {
|
||||
crate::sodium::sodium_init().unwrap();
|
||||
|
||||
// initialize secret and public key for the crypto server
|
||||
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||
StaticKEM::keygen(sk.secret_mut(), pk.secret_mut()).expect("unable to generate keys");
|
||||
stacker::grow(8 * 1024 * 1024, || {
|
||||
const OVERSIZED_MESSAGE: usize = ((MAX_MESSAGE_LEN as f32) * 1.2) as usize;
|
||||
type MsgBufPlus = Public<OVERSIZED_MESSAGE>;
|
||||
|
||||
CryptoServer::new(sk, pk)
|
||||
const PEER0: PeerPtr = PeerPtr(0);
|
||||
|
||||
let (mut me, mut they) = make_server_pair().unwrap();
|
||||
let (mut msgbuf, mut resbuf) = (MsgBufPlus::zero(), MsgBufPlus::zero());
|
||||
|
||||
// Process the entire handshake
|
||||
let mut msglen = Some(me.initiate_handshake(PEER0, &mut *resbuf).unwrap());
|
||||
loop {
|
||||
if let Some(l) = msglen {
|
||||
std::mem::swap(&mut me, &mut they);
|
||||
std::mem::swap(&mut msgbuf, &mut resbuf);
|
||||
msglen = test_incorrect_sizes_for_msg(&mut me, &*msgbuf, l, &mut *resbuf);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
me.osk(PEER0).unwrap().secret(),
|
||||
they.osk(PEER0).unwrap().secret()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// The determination of the message type relies on reading the first byte of the message. Only
|
||||
/// after that the length of the message is checked against the specified message type. This
|
||||
/// test ensures that nothing breaks in the case of an empty message.
|
||||
#[test]
|
||||
#[should_panic = "called `Result::unwrap()` on an `Err` value: received empty message, ignoring it"]
|
||||
fn handle_empty_message() {
|
||||
let mut crypt = init_crypto_server();
|
||||
let empty_rx_buf = [0u8; 0];
|
||||
let mut tx_buf = [0u8; 0];
|
||||
/// Used in handles_incorrect_size_messages() to first deliver many truncated
|
||||
/// and overlong messages, finally the correct message is delivered and the response
|
||||
/// returned.
|
||||
fn test_incorrect_sizes_for_msg(
|
||||
srv: &mut CryptoServer,
|
||||
msgbuf: &[u8],
|
||||
msglen: usize,
|
||||
resbuf: &mut [u8],
|
||||
) -> Option<usize> {
|
||||
resbuf.fill(0);
|
||||
|
||||
crypt.handle_msg(&empty_rx_buf, &mut tx_buf).unwrap();
|
||||
for l in 0..(((msglen as f32) * 1.2) as usize) {
|
||||
if l == msglen {
|
||||
continue;
|
||||
}
|
||||
|
||||
let res = srv.handle_msg(&msgbuf[..l], resbuf);
|
||||
assert!(matches!(res, Err(_))); // handle_msg should raise an error
|
||||
assert!(!resbuf.iter().find(|x| **x != 0).is_some()); // resbuf should not have been changed
|
||||
}
|
||||
|
||||
// Apply the proper handle_msg operation
|
||||
srv.handle_msg(&msgbuf[..msglen], resbuf).unwrap().resp
|
||||
}
|
||||
|
||||
fn keygen() -> Result<(SSk, SPk)> {
|
||||
// TODO: Copied from the benchmark; deduplicate
|
||||
let (mut sk, mut pk) = (SSk::zero(), SPk::zero());
|
||||
StaticKEM::keygen(sk.secret_mut(), pk.secret_mut())?;
|
||||
Ok((sk, pk))
|
||||
}
|
||||
|
||||
fn make_server_pair() -> Result<(CryptoServer, CryptoServer)> {
|
||||
// TODO: Copied from the benchmark; deduplicate
|
||||
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)?;
|
||||
Ok((a, b))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Bindings and helpers for accessing libsodium functions
|
||||
|
||||
use crate::{util::*, Result, RosenpassError};
|
||||
use crate::util::*;
|
||||
use anyhow::{ensure, Result};
|
||||
use libsodium_sys as libsodium;
|
||||
use log::trace;
|
||||
use static_assertions::const_assert_eq;
|
||||
@@ -25,11 +26,9 @@ const_assert_eq!(KEY_SIZE, libsodium::crypto_generichash_BYTES as usize);
|
||||
|
||||
macro_rules! sodium_call {
|
||||
($name:ident, $($args:expr),*) => { attempt!({
|
||||
if unsafe{libsodium::$name($($args),*)} > -1 {
|
||||
Ok(())
|
||||
}else{
|
||||
Err(RosenpassError::LibsodiumError(concat!("Error in libsodium's {}.", stringify!($name))))
|
||||
}
|
||||
ensure!(unsafe{libsodium::$name($($args),*)} > -1,
|
||||
"Error in libsodium's {}.", stringify!($name));
|
||||
Ok(())
|
||||
})};
|
||||
($name:ident) => { sodium_call!($name, ) };
|
||||
}
|
||||
@@ -252,12 +251,7 @@ pub fn mac16(key: &[u8], data: &[u8]) -> Result<[u8; 16]> {
|
||||
pub fn hmac_into(out: &mut [u8], key: &[u8], data: &[u8]) -> Result<()> {
|
||||
// Not bothering with padding; the implementation
|
||||
// uses appropriately sized keys.
|
||||
if key.len() != KEY_SIZE {
|
||||
return Err(crate::RosenpassError::BufferSizeMismatch {
|
||||
required_size: KEY_SIZE,
|
||||
actual_size: key.len(),
|
||||
});
|
||||
}
|
||||
ensure!(key.len() == KEY_SIZE);
|
||||
|
||||
const IPAD: [u8; KEY_SIZE] = [0x36u8; KEY_SIZE];
|
||||
let mut temp_key = [0u8; KEY_SIZE];
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Helper functions and macros
|
||||
use anyhow::{ensure, Context, Result};
|
||||
use base64::{
|
||||
display::Base64Display as B64Display, read::DecoderReader as B64Reader,
|
||||
write::EncoderWriter as B64Writer,
|
||||
};
|
||||
use log::error;
|
||||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
cmp::min,
|
||||
@@ -13,11 +13,19 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
coloring::{Public, Secret},
|
||||
Result,
|
||||
};
|
||||
use crate::coloring::{Public, Secret};
|
||||
|
||||
/// Xors a and b element-wise and writes the result into a.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass::util::xor_into;
|
||||
/// let mut a = String::from("hello").into_bytes();
|
||||
/// let b = b"world";
|
||||
/// xor_into(&mut a, b);
|
||||
/// assert_eq!(&a, b"\x1f\n\x1e\x00\x0b");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn xor_into(a: &mut [u8], b: &[u8]) {
|
||||
assert!(a.len() == b.len());
|
||||
@@ -61,7 +69,7 @@ pub fn cpy_min<T: BorrowMut<[u8]> + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, d
|
||||
#[macro_export]
|
||||
macro_rules! attempt {
|
||||
($block:expr) => {
|
||||
(|| -> crate::Result<_> { $block })()
|
||||
(|| -> ::anyhow::Result<_> { $block })()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -153,12 +161,8 @@ impl<R: Read> ReadExactToEnd for R {
|
||||
fn read_exact_to_end(&mut self, buf: &mut [u8]) -> Result<()> {
|
||||
let mut dummy = [0u8; 8];
|
||||
self.read_exact(buf)?;
|
||||
if self.read(&mut dummy)? != 0 {
|
||||
error!("File too long!");
|
||||
Err(crate::RosenpassError::RuntimeError)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
ensure!(self.read(&mut dummy)? == 0, "File too long!");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,11 +183,11 @@ trait StoreValue {
|
||||
}
|
||||
|
||||
trait StoreSecret {
|
||||
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()>;
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()>;
|
||||
}
|
||||
|
||||
impl<T: StoreValue> StoreSecret for T {
|
||||
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
self.store(path)
|
||||
}
|
||||
}
|
||||
@@ -192,10 +196,9 @@ impl<const N: usize> LoadValue for Secret<N> {
|
||||
fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||
let mut v = Self::random();
|
||||
let p = path.as_ref();
|
||||
fopen_r(p)?.read_exact_to_end(v.secret_mut()).map_err(|e| {
|
||||
error!("Could not load file {p:?}");
|
||||
e
|
||||
})?;
|
||||
fopen_r(p)?
|
||||
.read_exact_to_end(v.secret_mut())
|
||||
.with_context(|| format!("Could not load file {p:?}"))?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
@@ -213,16 +216,13 @@ impl<const N: usize> LoadValueB64 for Secret<N> {
|
||||
// not worth it right now.
|
||||
b64_reader(&mut fopen_r(p)?)
|
||||
.read_exact(v.secret_mut())
|
||||
.map_err(|e| {
|
||||
error!("Could not load base64 file {p:?}");
|
||||
e
|
||||
})?;
|
||||
.with_context(|| format!("Could not load base64 file {p:?}"))?;
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> StoreSecret for Secret<N> {
|
||||
unsafe fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
fn store_secret<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
std::fs::write(path, self.secret())?;
|
||||
Ok(())
|
||||
}
|
||||
48
rp
48
rp
@@ -197,7 +197,7 @@ exchange() {
|
||||
lip="${listen%:*}";
|
||||
lport="${listen/*:/}";
|
||||
if [[ "$lip" = "$lport" ]]; then
|
||||
lip="[0::0]"
|
||||
lip="[::]"
|
||||
fi
|
||||
shift;;
|
||||
-h | -help | --help | help) usage; return 0;;
|
||||
@@ -209,15 +209,41 @@ exchange() {
|
||||
fatal "Needs at least one peer specified"
|
||||
fi
|
||||
|
||||
frag "
|
||||
# Create the Wireguard interface
|
||||
ip link add dev $(enquote "${dev}") type wireguard || true"
|
||||
# os dependent setup
|
||||
case "$OSTYPE" in
|
||||
linux-*) # could be linux-gnu or linux-musl
|
||||
frag "
|
||||
# Create the WireGuard interface
|
||||
ip link add dev $(enquote "${dev}") type wireguard || true"
|
||||
|
||||
cleanup "
|
||||
ip link del dev $(enquote "${dev}") || true"
|
||||
cleanup "
|
||||
ip link del dev $(enquote "${dev}") || true"
|
||||
|
||||
frag "
|
||||
ip link set dev $(enquote "${dev}") up"
|
||||
frag "
|
||||
ip link set dev $(enquote "${dev}") up"
|
||||
;;
|
||||
|
||||
freebsd*)
|
||||
frag "
|
||||
# load the WireGuard kernel module
|
||||
kldload -n if_wg || fatal 'Cannot load if_wg kernel module'"
|
||||
|
||||
frag "
|
||||
# Create the WireGuard interface
|
||||
ifconfig wg create name $(enquote "${dev}") || true"
|
||||
|
||||
cleanup "
|
||||
ifconfig $(enquote "${dev}") destroy || true"
|
||||
|
||||
frag "
|
||||
ifconfig $(enquote "${dev}") up"
|
||||
;;
|
||||
|
||||
*)
|
||||
fatal "Your system $OSTYPE is not yet supported. We are happy to receive patches to address this :)"
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
frag "
|
||||
# Deploy the classic wireguard private key
|
||||
@@ -255,7 +281,7 @@ exchange() {
|
||||
local arg; arg="$1"; shift
|
||||
case "${arg}" in
|
||||
peer) set -- "peer" "$@"; break;; # Next peer
|
||||
endpoint) ip="${1%:*}"; port="${1/*:/}"; shift;;
|
||||
endpoint) ip="${1%:*}"; port="${1##*:}"; shift;;
|
||||
persistent-keepalive) keepalive="${1}"; shift;;
|
||||
allowed-ips) allowedips="${1}"; shift;;
|
||||
-h | -help | --help | help) usage; return 0;;
|
||||
@@ -326,7 +352,9 @@ main() {
|
||||
verbose=0
|
||||
scriptdir="$(dirname "${script}")"
|
||||
gitdir="$(detect_git_dir)" || true
|
||||
nixdir="$(readlink -f result/bin/rp | grep -Pio '^/nix/store/[^/]+(?=/bin/[^/]+)')" || true
|
||||
if [[ -d /nix ]]; then
|
||||
nixdir="$(readlink -f result/bin/rp | grep -Pio '^/nix/store/[^/]+(?=/bin/[^/]+)')" || true
|
||||
fi
|
||||
binary="$(find_rosenpass_binary)"
|
||||
|
||||
# Parse command
|
||||
|
||||
131
src/lib.rs
131
src/lib.rs
@@ -1,131 +0,0 @@
|
||||
use protocol::{HandshakeStateMachine, PeerId, PeerPtr, SessionId};
|
||||
|
||||
#[macro_use]
|
||||
pub mod util;
|
||||
#[macro_use]
|
||||
pub mod sodium;
|
||||
pub mod coloring;
|
||||
#[rustfmt::skip]
|
||||
pub mod labeled_prf;
|
||||
pub mod app_server;
|
||||
pub mod cli;
|
||||
pub mod config;
|
||||
pub mod msgs;
|
||||
pub mod pqkem;
|
||||
pub mod prftree;
|
||||
pub mod protocol;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum RosenpassError {
|
||||
#[error("error in OQS")]
|
||||
Oqs,
|
||||
|
||||
#[error("error from external library while calling OQS")]
|
||||
OqsExternalLib,
|
||||
|
||||
#[error("error while calling into libsodium")]
|
||||
LibsodiumError(&'static str),
|
||||
|
||||
#[error("buffer size mismatch, required {required_size} but only found {actual_size}")]
|
||||
BufferSizeMismatch {
|
||||
required_size: usize,
|
||||
actual_size: usize,
|
||||
},
|
||||
|
||||
#[error("invalid message type")]
|
||||
InvalidMessageType(u8),
|
||||
|
||||
#[error("peer id {0:?} already taken")]
|
||||
PeerIdAlreadyTaken(PeerId),
|
||||
|
||||
#[error("session id {0:?} already taken")]
|
||||
SessionIdAlreadyTaken(SessionId),
|
||||
|
||||
#[error("{0}")]
|
||||
NotImplemented(&'static str),
|
||||
|
||||
#[error("{0}")]
|
||||
ConfigError(String),
|
||||
|
||||
#[error("see last log messages")]
|
||||
RuntimeError,
|
||||
|
||||
#[error("{0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("{0}")]
|
||||
TomlDeserError(#[from] toml::de::Error),
|
||||
|
||||
#[error("{0}")]
|
||||
TomlSerError(#[from] toml::ser::Error),
|
||||
|
||||
#[error("invalid session id {0:?} was used")]
|
||||
InvalidSessionId(SessionId),
|
||||
|
||||
#[error("no session available")]
|
||||
NoSession,
|
||||
#[error("the peer {0:?} does not exist")]
|
||||
NoSuchPeer(PeerPtr),
|
||||
|
||||
#[error("the peer id {0:?} does not exist")]
|
||||
NoSuchPeerId(PeerId),
|
||||
|
||||
#[error("the session {0:?} does not exist")]
|
||||
NoSuchSessionId(SessionId),
|
||||
|
||||
#[error("no current handshake with peer {0:?}")]
|
||||
NoCurrentHs(PeerPtr),
|
||||
// TODO implement Display for Peer/Session ptr?
|
||||
#[error("message seal broken")]
|
||||
SealBroken,
|
||||
|
||||
#[error("received empty message")]
|
||||
EmptyMessage,
|
||||
|
||||
#[error("biscuit with invalid number")]
|
||||
InvalidBiscuitNo,
|
||||
|
||||
#[error("got unexpected message")]
|
||||
UnexpectedMessage {
|
||||
session: SessionId,
|
||||
expected: Option<HandshakeStateMachine>,
|
||||
got: Option<HandshakeStateMachine>,
|
||||
},
|
||||
|
||||
#[error("???")]
|
||||
StaleNonce,
|
||||
}
|
||||
|
||||
/// Rosenpass Result type
|
||||
pub type Result<T> = core::result::Result<T, RosenpassError>;
|
||||
|
||||
impl RosenpassError {
|
||||
/// Helper function to check a buffer size
|
||||
fn check_buffer_size(required_size: usize, actual_size: usize) -> Result<()> {
|
||||
if required_size != actual_size {
|
||||
Err(Self::BufferSizeMismatch {
|
||||
required_size,
|
||||
actual_size,
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait to attach function calls to foreign types.
|
||||
trait RosenpassMaybeError {
|
||||
/// Checks whether something is an error or not
|
||||
fn to_rg_error(&self) -> Result<()>;
|
||||
}
|
||||
|
||||
impl RosenpassMaybeError for oqs_sys::common::OQS_STATUS {
|
||||
fn to_rg_error(&self) -> Result<()> {
|
||||
use oqs_sys::common::OQS_STATUS;
|
||||
match self {
|
||||
OQS_STATUS::OQS_SUCCESS => Ok(()),
|
||||
OQS_STATUS::OQS_ERROR => Err(RosenpassError::Oqs),
|
||||
OQS_STATUS::OQS_EXTERNAL_LIB_ERROR_OPENSSL => Err(RosenpassError::OqsExternalLib),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user